Php 如何使用XMLReader读取具有未定义命名空间的XML文件?

Php 如何使用XMLReader读取具有未定义命名空间的XML文件?,php,xml,parsing,xmlreader,xml-namespaces,Php,Xml,Parsing,Xmlreader,Xml Namespaces,我对解析XML文件比较陌生,正在尝试使用XMLReader读取大型XML文件 <?xml version="1.0" encoding="UTF-8"?> <ShowVehicleRemarketing environment="Production" lang="en-CA" release="8.1-Lite" xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalo

我对解析XML文件比较陌生,正在尝试使用XMLReader读取大型XML文件

<?xml version="1.0" encoding="UTF-8"?>
<ShowVehicleRemarketing environment="Production" lang="en-CA" release="8.1-Lite" xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd">
  <ApplicationArea>
    <Sender>
      <Component>Component</Component>
      <Task>Task</Task>
      <ReferenceId>w5/cron</ReferenceId>
      <CreatorNameCode>CreatorNameCode</CreatorNameCode>
      <SenderNameCode>SenderNameCode</SenderNameCode>
      <SenderURI>http://www.example.com</SenderURI>
      <Language>en-CA</Language>
      <ServiceId>ServiceId</ServiceId>
    </Sender>
    <CreationDateTime>CreationDateTime</CreationDateTime>
    <Destination>
      <DestinationNameCode>example</DestinationNameCode>
    </Destination>
  </ApplicationArea>
...

组成部分
任务
w5/cron
CreatorNameCode
SenderNameCode
http://www.example.com
恩卡
服务ID
创作时间
例子
...
我收到以下错误

ErrorException[警告]:XMLReader::read()[XMLReader.read]:compress.zlib://D:/WebDev/example/local/public/./upload/example.xml.gz:2:命名空间错误:未定义ShowVehiclerMarketing上schemaLocation的命名空间前缀xsi


我四处搜索,找不到关于使用XMLReader读取带有名称空间的XML文件的有用信息——如果我需要定义名称空间,我该如何定义名称空间。。帮个小忙?相关资源的链接?

需要定义
xsi
名称空间。例如

<ShowVehicleRemarketing
  environment="Production"
  lang="en-CA"
  release="8.1-Lite"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.starstandards.org/STAR/STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"
>
然后,compress.zlib包装器读取的内容通过DarnFilter“路由”,DarnFilter必须找到可以插入xmlns:xsi声明的(第一个)位置。但这相当混乱,需要一些人来承担正确的操作(例如,理论上,bucket A可以包含
xs
、bucket B
i:schem
和bucket C
aLocation=“


更新2:这里是一个特殊的php过滤器示例,它插入xsi命名空间声明。大多数未经测试(使用我运行的一个测试;-)且未记录。将其视为概念证明,而不是生产代码

<?php
stream_filter_register('darn', 'DarnFilter');
$src = 'php://filter/read=darn/resource=compress.zlib://d:/test.xml.gz';

$r = new XMLReader;
$r->open($src);
while($r->read()) {
  echo '.';
}

class DarnFilter extends php_user_filter {
  protected $buffer='';
  protected $status = PSFS_FEED_ME;

  public function filter($in, $out, &$consumed, $closing)
  {
    while ( $bucket = stream_bucket_make_writeable($in) ) {
      $consumed += $bucket->datalen;
      if ( PSFS_PASS_ON == $this->status ) {
        // we're already done, just copy the content
        stream_bucket_append($out, $bucket);
      }
      else {
        $this->buffer .= $bucket->data;
        if ( $this->foo() ) {
          // first element found
          // send the current buffer          
          $bucket->data = $this->buffer;
          $bucket->datalen = strlen($bucket->data);
          stream_bucket_append($out, $bucket);
          $this->buffer = null;
          // no need for further processing
          $this->status = PSFS_PASS_ON;
        }
      }
    }
    return $this->status;
  }

  /* looks for the first (root) element in $this->buffer
  *  if it doesn't contain a xsi namespace decl inserts it
  */
  protected function foo() {
    $rc = false;
    if ( preg_match('!<([^?>\s]+)\s?([^>]*)>!', $this->buffer, $m, PREG_OFFSET_CAPTURE) ) {
      $rc = true;
      if ( false===strpos($m[2][0], 'xmlns:xsi') ) {
        echo ' inserting xsi decl ';
        $in = '<'.$m[1][0]
          . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
          . $m[2][0] . '>';    
        $this->buffer = substr($this->buffer, 0, $m[0][1])
          . $in
          . substr($this->buffer, $m[0][1] + strlen($m[0][0]));
      }
    }
    return $rc;
  }
}

在将XML传递给
XMLReader
之前,您可以
file\u获取内容
str\u替换
XML

为xsi前缀插入所需的命名空间声明:

$reader = new XMLReader;
$reader->xml(str_replace(
    '<ShowVehicleRemarketing',
    '<ShowVehicleRemarketing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
    file_get_contents('http://example.com/data.xml')));

但是,如果文档中有更多的前缀,则必须替换所有前缀。

要么修复写入格式错误的XML的内容,要么编写一个单独的工具来稍后执行修复。(不必同时将其全部读入内存,必要时—将数据流输入/输出,可能一次读写一行。)


这样,您阅读代码时就不必担心试图对数据做一些有用的事情,同时又对其进行修复。

xsi名称空间通常保留用于:

如果不是,则说明您的XML文件不符合XML+NS,无法解析。因此,您应该在源文档中解决此问题


关于xsi的一个注意事项:它甚至比其他一些可能的名称空间更重要,因为它将验证解析器指向XML模式的正确模式位置。

好吧。假设XML是远程的,我无法更改它——有没有办法忽略文档似乎格式不正确(即缺少名称空间定义)这一事实ion?我不认为php的XMLReader可以忽略此类错误,也不能“注入”名称空间声明。看起来您必须更改文档,可能是动态的,但这并不能提高性能。php是您唯一的选择吗?例如,dotnet XMLReader可以用已经存在的XmlParserContext进行初始化。”包含“预定义的名称空间。请看,PHP是唯一的选项——您认为,在我尝试读取文档之前,有没有一种方法可以在不将整个内容加载到内存的情况下修改文档?还有一些复杂的问题--它是gzip压缩的,~300Mb未压缩的。。事情开始变得复杂/绝望,请参阅更新。听起来这些需求不在php的最佳范围之内。请随意解释为什么php是唯一的选择(也可以随意拒绝;-)@Volker我在评论中也建议使用流包装器。str_是否也可以替换其中的名称空间声明。如果文件不是~300Mb,那就可以了也许我应该探索一些选项,在不将整个文件加载到内存的情况下尝试重新写入?@Felix-hmm,我从未尝试过,但是您可以使用注册自定义流过滤器,在XmlReader处理数据之前对其进行修改。可能重复的虽然我比上一个问题更喜欢这个问题的标题,但它仍然是重复的。对不起,它甚至不仅仅是一个复制品,而是同一个用户在两小时内再次问同样的问题……你。请不要这样做,这对任何人都没有帮助,是不好的SEO(注意:允许重复,但不要反复问相同的问题)。有关如何使用此网站的信息,请参阅。
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
// prime the XMLReader with the xsi namespace
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");

using ( XmlReader reader = XmlTextReader.Create(
  new GZipStream(new FileStream(@"\test.xml.gz", FileMode.Open, FileAccess.Read), CompressionMode.Decompress),
  new XmlReaderSettings(),
  new XmlParserContext(null, nsmgr, null, XmlSpace.None)
)) {
  while (reader.Read())
  {
    System.Console.Write('.');
  }
}
$reader = new XMLReader;
$reader->xml(str_replace(
    '<ShowVehicleRemarketing',
    '<ShowVehicleRemarketing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
    file_get_contents('http://example.com/data.xml')));
$reader->xml(str_replace(
    'xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"',
    '',
    file_get_contents('http://example.com/data.xml')));
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'