【问题标题】:How to read an XML file with an undefined namespace with XMLReader?如何使用 XMLReader 读取具有未定义命名空间的 XML 文件?
【发布时间】:2010-08-24 08:32:52
【问题描述】:

我对解析 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>
...

我收到以下错误

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

我已经四处搜索,找不到太多有用的信息来使用 XMLReader 读取带有命名空间的 XML 文件——如果这实际上是我需要做的,我将如何定义命名空间.. 帮助不大?相关资源的链接?

【问题讨论】:

标签: php xml parsing xmlreader xml-namespaces


【解决方案1】:

需要定义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"
>

更新:您可以 write a user defined filter 然后让 XMLReader use that filter,类似:

stream_filter_register('darn', 'DarnFilter');
$src = 'php://filter/read=darn/resource=compress.zlib://something.xml.gz';
$reader->open($src);

compress.zlib 包装器读取的内容然后通过 DarnFilter “路由”,该过滤器必须找到可以插入 xmlns:xsi 声明的(第一个)位置。但这很混乱,需要一些负担才能正确完成(例如,理论上存储桶 A 可以包含 xs、存储桶 B i:schem 和存储桶 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;
  }
}

更新 3:这是一个用 C# 编写的临时解决方案

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('.');
  }
}

【讨论】:

  • 好吧.. 所以说 XML 是远程的,我无法更改它——有没有办法忽略文档似乎格式错误的事实,即缺少命名空间定义?
  • 我不认为 php 的 XMLReader 可以选择忽略这种错误或“注入”命名空间声明的方法。看起来您必须更改文档,也许是即时更改,但这并不能完全提高性能。 PHP是你唯一的选择吗?例如。 dotnet XMLReader 可以使用已经“包含”预定义命名空间的 XmlParserContext 进行初始化。见msdn.microsoft.com/en-us/library/xc8bact5.aspx
  • PHP 是唯一的选择——你认为有没有办法在我尝试读取文档而不将整个内容加载到内存中之前对其进行更改?一些更复杂的问题——它是 gzip 和 ~300Mb 未压缩的。事情开始变得复杂/绝望
  • 查看更新。听起来这些要求不在 php 的“甜蜜点”之内。随意解释为什么 php 是唯一的选择(也可以随意拒绝 ;-))
  • @Volker 我也建议在我的 cmets 中使用 Stream Wrapper。也可以在其中 str_replace 命名空间声明。
【解决方案2】:

您可以在将 XML 传递给 XMLReader 之前将其 file_get_contentsstr_replace

为 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')));

另一种选择是删除schemaLocation 属性:

$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')));

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

【讨论】:

  • sigh 如果文件不是 ~300Mb,那会很好用 也许我应该探索一些选项来尝试重写 而不将整个文件加载到内存中?
  • @Felix 嗯,我从未尝试过,但您可以使用 libxml functions 注册一个自定义流过滤器,在 XmlReader 处理数据之前对其进行修改。
【解决方案3】:

要么修复任何写出格式错误的 XML,要么编写一个单独的工具来稍后执行修复。 (它不一定必须同时将所有数据读入内存 - 将数据流入/流出,可能一次读取和写入一行。)

这样您的阅读代码就不必担心尝试对数据做一些有用的事情同时修复它。

【讨论】:

    【解决方案4】:

    xsi 命名空间通常保留用于Schema Instance Namespace

    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    

    如果不是,则您的 XML 文件不符合 XML+NS 且无法解析。所以你应该在源文档中解决这个问题。

    关于 xsi 的说明:它比一些可能的其他命名空间更重要,因为它会将验证解析器引导到 XML 架构的正确架构位置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多