使用XMLReader选择父节点

时间:2015-04-09 13:13:52

标签: php xml xpath xml-parsing

我不得不重写程序的一部分,使用XMLReader选择XML文件的一部分进行处理。

以这个简化的XML为例:

<odds>
    <sport>
        <region>
            <group>
                <event name="English Championship 2014-15" eventid="781016.1">
                    <bet name="Kazanan" betid="12377108.1">
                        <selection selectionid="52411062.1"/>
                        </selection>
                    </bet>
                </event>
            </group>
        </region>
    </sport>
</odds> 

此次致电xpath()

$bets = $xml->xpath(
    "//odds/sport/region/group/event/bet/selection[contains(@selectionid,'".$selectionToFind."')]/.."
    );

将选择整个<bet>节点及其子节点(<selection>节点)。

但是,我的代码只选择一个具有给定<selection>的{​​{1}}节点:

selectionid

如何使用$reader = new XMLReader; $reader->open('file.xml'); while($reader->read()) { $event = $reader->getAttribute($value); if ($event == 781016.1 ) { $node = new SimpleXMLElement($reader->readOuterXML()); var_dump($node); break; } } 复制xpath()的行为,以便我选择XMLReader节点及其子节点而不仅仅是一个<bet>子节点?

我想这个问题归结为:我可以通过子项的属性值选择整个父节点<selection>,例如<bet>

3 个答案:

答案 0 :(得分:1)

[忽略SimpleXML解决方案并向下看XMLReader一个]

我建议使用SimpleXMLElement :: xpath方法。

http://php.net/manual/en/simplexmlelement.xpath.php

$xml = new SimpleXMLElement($xml_string);

/* Search for <a><b><c> */
$result = $xml->xpath("/odds/sport/region/group/event/bet");

$ result将包含所有“打赌”的孩子。注意:

// XMLReader解决方案**********************

$reader = new XMLReader;
$reader->open('file.xml');
$parent_element = null;

while($reader->read()) {
    $selectionid = $reader->getAttribute('selectionid'); 

    if ($selectionid == '52411062.1' ) {
        // use the parent of the node with attribute 'selectionid' = '52411062.1'
        $node = $parent_element;
        var_dump($node);
        break;
    }
    elseif ($reader->name === 'bet') { )
    {
        // store parent element
        $parent_element = new SimpleXMLElement($reader->readOuterXML());
    }
}

答案 1 :(得分:0)

据说

DOMXPath在性能方面比SimpleXML更强大(它有其他优点,例如它可以正确处理命名空间)。有关PHP中几个XPath库的讨论,请参阅示例this IBM article

在使用DOMXPath时,如果您的性能问题仍然存在(或仍然严重),我很好奇:

<?php

$doc = new DOMDocument;
$doc->load('sample.xml');
$xpath = new DOMXPath($doc);

$nodes = $xpath->query("/odds/sport/region/group/event/bet[selection/@selectionid = '52411062.1']");

foreach ($nodes as $node)
{
   print $xml = $node->ownerDocument->saveXML($node);
}
?>

结果是将您显示的小片段作为输入

<bet name="Kazanan" betid="12377108.1">
    <selection selectionid="52411062.1"/>
</bet>

如果这没有帮助,你真的不得不求助于一个基于事件的(拉式)XML解析器,它不会将整个文档读入内存 - 正如Yasen所暗示的那样。

答案 2 :(得分:0)

XMLReader可以expand()将当前节点转换为DOMNode。这将仅将节点及其后代加载到内存中。

之后,您可以使用DOMXPath实例或将节点转换为SimpleXMLElement

$reader = new XMLReader();
$reader->open('data:/text/xml,'.urlencode($xml));

$dom = new DOMDocument();
$xpath = new DOMXpath($dom);

while($reader->read()) {
  if (
    $reader->nodeType == XMLReader::ELEMENT && 
    $reader->localName == 'bet'
  ) {
    $bet= $reader->expand($dom);
    if ($xpath->evaluate('count(selection[@selectionid = "52411062.1"]) > 0', $bet)) {
      var_dump($dom->saveXml($bet));
    }
  }
}

您将始终必须决定在XMLReader中实现哪个部分以及在DOM / SimpleXML中实现哪个部分。在XMLReader中,您必须验证节点并维护状态,但可以避免加载数据。在解析的某个时刻,XML片段将足够小,您可以使用expand()