2010-08-24 56 views
2

我对解析XML文件比较陌生,并试图用XMLReader读取大型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> 
... 

我recieving以下错误

ErrorException [警告]:的XMLReader ::读()[xmlreader.read]:compress.zlib:// d:/ Webdev的/示例/本地/public/../upload/example.xml.gz:2:命名错误:对的schemaLocation命名空间前缀XSI上ShowVehicleRemarketing没有定义

我已搜索周围,找不到使用的有用信息XMLReader用命名空间读取XML文件 - 我将如何去定义一个名称步伐,如果这实际上是我需要做的......帮助不大?链接到相关的资源?

+1

可能重复的[如何读取具有XMLReader命名空间的XML文件?](http://stackoverflow.com/questions/3554724/how-to-read-an-xml-file-that-has- a-namespace-with-xmlreader) – VolkerK 2010-08-24 11:03:47

+2

尽管我比以前更喜欢这个问题的标题,但它仍然是重复的。抱歉。 – VolkerK 2010-08-24 11:07:25

+0

它甚至不*只是*一个重复的,它是同一用户在两个小时内再次询问同一个问题...... – Abel 2010-08-24 14:52:18

回答

5

需要定义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声明的(第一个)位置。但是,这是相当混乱,并会采取一些负担得起这样做的权利(如理论上桶中可能含有xs,斗乙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('.'); 
    } 
} 
+0

好吧..所以说XML是远程的,我不能改变它 - 有没有办法忽略文件​​似乎是格式不正确的事实,即缺少名称空间定义? – JeremyFelix 2010-08-24 08:42:43

+0

我不认为php的XMLReader有一个选项来忽略那种错误或“注入”命名空间声明的方法。看起来你必须改变这些文件,可能是即时的,但这并不能提高性能。 PHP是你唯一的选择?例如。 dotnet XMLReader可以用已经“包含”预定义的命名空间的XmlParserContext来初始化。请参阅http://msdn.microsoft.com/en-us/library/xc8bact5.aspx – VolkerK 2010-08-24 09:58:03

+0

PHP是唯一的选择 - 是否有一种方法,你认为,在我尝试阅读它之前修改文档而不加载整个事情进入记忆?一些进一步的并发症 - 它的gzip和〜300Mb未压缩。事情开始看起来复杂/无望 – JeremyFelix 2010-08-24 10:08:36

1

一个特设的解决方案可以file_get_contentsstr_replace它传递给XMLReader前的XML。

请插入的XSI前缀所需的命名空间declararation:

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

但是,如果文档中多个前缀,你将不得不全部替换它们。

+0

*叹气*如果文件不是〜300Mb,那将工作正常 也许我应该探索一些选项来尝试重写而不将整个文件加载到内存中? – JeremyFelix 2010-08-24 09:55:07

+0

@Felix嗯,我从来没有尝试过,但你可能可以使用[libxml函数](http://de.php.net/manual/en/function.libxml-set-streams-context.php )注册一个自定义流过滤器,在XmlReader处理它之前修改数据。 – Gordon 2010-08-24 10:36:32

0

要么修复写出格式不正确的XML的问题,要么编写一个单独的工具来稍后执行修复。 (它不必一次将所有内容全部读入内存,必须 - 将数据流入/流出,可能一次读取和写入一行。)

这样你的阅读代码不需要担心试图做一些有用的数据在同一时间修复它。

1

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

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

,如果不是的话,你的XML文件不是XML + NS符合规定,不能被解析。所以你应该在源文件中解决这个问题。

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

相关问题