【问题标题】:Reading Child Nodes with XMLReader使用 XMLReader 读取子节点
【发布时间】:2013-06-23 16:25:11
【问题描述】:

我正在尝试编写一个 XMLReader/SimpleXML 混合函数来读取一个非常大 (700MB) 的 XML 文件。 XML 格式如下:

<Items>
    <Item>
         <ItemKey>ABCDEF123</ItemKey>
         <Name>
             <English>An Item Name</English>
             <German>An Item Name In German</German>
             <French>An Item Name In French</French>
         </Name>
         <Description>
             <English>An Item Description</English>
             <German>An Item Description In German</German>
             <French>An Item Description In French</French>
         </Description>
    </Item>
    <Item>
         <ItemKey>GHIJKL456</ItemKey>
         <Name>
             <English>Another Item Name</English>
             <German>Another Item Name In German</German>
             <French>Another Item Name In French</French>
         </Name>
         <Description>
             <English>Another Item Description</English>
             <German>Another Item Description In German</German>
             <French>Another Item Description In French</French>
         </Description>
    </Item>
</Items>

到目前为止我为此编写的代码:

$xml = new XMLReader();
if(!$xml->open('testitems.xml')){
    die('Failed to open file!');
} else {
    echo 'File opened';
}

$items = array();

while ($xml->read()){
    if($xml->nodeType == XMLReader::ELEMENT){
        if ($xml->name == 'Item'){
            $item = array();
        }

        if ($xml->name == 'ItemKey'){
            $xml->read();
            $item['itemKey'] = $xml->value;
        }
        if ($xml->name == 'Name'){
            $sxml = new SimpleXMLElement($xml->readOuterXml());
            $englishName = $sxml->English;
            $item['englishName'] = $englishName;
        }
    }
    if($xml->nodeType == XMLReader::END_ELEMENT){
        if ($xml->name == 'Item'){
            $items[] = $item;
        }
    }
}
var_dump($items);
$xml->close();

但是,当 ItemKey 节点值被插入到数组中时,英文名称不是,我似乎无法正确访问该节点。我只会将 XMLReader 用于所有内容,但由于从我的谷歌搜索中重复出现英文节点(一个用于名称,另一个用于描述),到目前为止 SimpleXML 似乎是前进的方向,但还没有乐趣。

有什么建议吗?有什么好的指南吗?与许多其他 PHP 功能相比,php.net 上的 XMLReader 文档严重缺乏,而且通常似乎很难找到清晰简洁的详细指南。

【问题讨论】:

    标签: php xml simplexml xmlreader


    【解决方案1】:

    如果您仍然可以构建该数组,那么您的 XML 文件可能不会那么大 :)。例如,尝试使用 simplexml 加载整个文件,您可能会惊讶于它不会消耗那么多内存。

    无论如何,如果您仍想使用 XMLReader,我经常建议使用我的 XMLReader Iterator library,它能够遍历 XMLReader 以访问元素、子元素并执行诸如将片段转换为 SimpleXMLElements 之类的操作。

    以下是与您上面的示例几乎相同的示例:

    require('xmlreader-iterators.php'); // https://github.com/hakre/XMLReaderIterator/tree/master/build/include
    
    $xmlFile = "xmlreader-17262798.xml";
    
    $reader = new XMLReader();
    $reader->open($xmlFile);
    
    /* @var $itemIterator XMLReaderNode[] */
    $itemIterator = new XMLElementIterator($reader, 'Item');
    
    $items = array();
    
    foreach ($itemIterator as $item) {
        $xml     = $item->asSimpleXML();
        $items[] = array(
            'itemKey'     => (string)$xml->ItemKey,
            'englishName' => (string)$xml->Name->English,
        );
    }
    

    当您在演示数据上运行它时,生成的 $items 数组是:

    Array
    (
        [0] => Array
            (
                [itemKey] => ABCDEF123
                [englishName] => An Item Name
            )
    
        [1] => Array
            (
                [itemKey] => GHIJKL456
                [englishName] => Another Item Name
            )
    
    )
    

    从技术上讲,您不需要使用该库,它只在XMLReader 上运行,因此它不会改变XMLReader 的工作方式。这是一个附加组件。

    为什么它在您的特定情况下不起作用很难说,您的代码确实在我的计算机上完美运行:

    Array
    (
        [0] => Array
            (
                [itemKey] => ABCDEF123
                [englishName] => SimpleXMLElement Object
                    (
                        [0] => An Item Name
                    )
    
            )
    
        [1] => Array
            (
                [itemKey] => GHIJKL456
                [englishName] => SimpleXMLElement Object
                    (
                        [0] => Another Item Name
                    )
    
            )
    
    )
    

    正如$items(您的代码)的print_r 输出所示,englishName 键设置为 simplexml 元素。您可能想像我在示例中所做的那样将它们转换为字符串(这两个(string) 部分)以在其中使用字符串而不是 SimpleXMLElements,这可能是您的问题。如果没有,请检查您的 libxml 版本:

    var_dump(LIBXML_DOTTED_VERSION); # string(5) "2.7.8"
    

    并报告回来(即库XMLReader 是基于)。同时调试您的 SimpleXMLElement (var_dump($sxml-&gt;asXML());),以便检查预期的 XML 是否已加载。

    顺便说一句,我建议的图书馆。如果您想快速尝试,还附带a single include file。

    上次我建议图书馆在:


    编辑: 一个额外的混合版本,没有显示使用 next() 的库,这在您总是迭代同名的兄弟姐妹时很有用:&lt;Item&gt;:

    $xmlFile = "xmlreader-17262798.xml";
    
    $reader = new XMLReader();
    $reader->open($xmlFile);
    
    $reader->read() && $reader->read(); // init and position onto first element
    
    $items = array();
    while ($reader->next('Item')) {
        $node = new SimpleXMLElement($reader->readOuterXML());
    
        $items[] = array(
            'itemkey'     => $node->ItemKey,
            'englishName' => $node->Name->English,
            'englishDesc' => $node->Description->English,
        );
    }
    

    【讨论】:

      【解决方案2】:

      没关系,想通了。对于其他陷入困境的人:

      $xml = new XMLReader();
      if(!$xml->open('Items.xml')){
          die('Failed to open file!');
      } else {
          echo 'File opened';
      }
      
      $items = array();
      
      while ($xml->read() && $xml->name !== "Item");
      while ($xml->name === "Item") {
          $item = array();
          $node = new SimpleXMLElement($xml->readOuterXML());
          $item['itemkey'] = $node->ItemKey;
          $item['englishName'] = $node->Name->English;
          $item['englishDesc'] = $node->Description->English;
          $items[] = $item;
      }
      

      【讨论】:

      • 这基本上也是我所拥有的,但是我仍然不清楚为什么您的原始代码不起作用。正如我已经回答的那样,我确实运行了它并且它对我有用。在这里,您几乎可以执行相同的操作,并且现在也可以运行。
      • 是的,我最初写了一些类似于这个工作版本的东西,它不会通过 SimpleXML 给我节点值,然后突然之间就这样了。奇怪,但在编码时似乎很常见,哈哈。可能是一个小的语法错误。顺便说一句,我之前看到您发布过您的解决方案,并注意到有人评论说 XMLReader/SimpleXML 混合(我采用的路线)比您的纯 XMLReader 方法更快,您是否尝试了这两种方法?哪个更快?我设法在 53 秒内解析了大约 90k 条记录,这比我的仅使用 SimpleXML 的方法(2 小时!)有了很大的改进
      • 好吧,混合方法是我的答案的一部分,如果你仔细观察的话:$item-&gt;asSimpleXML(); 会返回 - 猜 :) - 是的。它只是在其中实现,因此我不再需要关心。由于 libxml 版本,它还兼容不支持 readOuterXML() 的系统。由于迭代器是一个附加组件,因此您可以说这基本上是相同的。在你的情况下,你可以做一个额外的技巧,我会破解它并扩展我的答案只是为了便于阅读。
      • 抱歉被另一个问题分心了,您可以在我的回答末尾找到更新。如果您不想使用迭代器,请在此处考虑如何使用next()。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-29
      相关资源
      最近更新 更多