使用 XMLReader 选择父节点
Selecting parent nodes with XMLReader
我不得不重写部分程序以使用 XMLReader 来处理 XML 文件的 select 部分。
以这个简化的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."')]/.."
);
将 select 整个 <bet>
节点及其子节点(<selection>
节点)。
但是,我的代码 select 只有一个 <selection>
节点具有给定的 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;
}
}
如何用 XMLReader
复制 xpath()
的行为,以便我 select <bet>
节点及其子节点,而不仅仅是一个 <selection>
子节点?
我想这个问题可以归结为:我可以 select 整个父节点 <bet>
通过子节点的属性值,例如<selection selectionid="[some_value]">
?
[忽略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 将包含 'bet' 注释的所有子项。
// 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());
}
}
DOMXPath
is said to be more robust than SimpleXML
with respect to performance (it has other advantages, e.g. it can properly deal with namespaces). See for example this IBM article 在 PHP.
中讨论了几个 XPath 库
我很好奇使用 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 所建议的那样将整个文档读入内存。
XML读者可以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()
.
我不得不重写部分程序以使用 XMLReader 来处理 XML 文件的 select 部分。
以这个简化的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."')]/.."
);
将 select 整个 <bet>
节点及其子节点(<selection>
节点)。
但是,我的代码 select 只有一个 <selection>
节点具有给定的 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;
}
}
如何用 XMLReader
复制 xpath()
的行为,以便我 select <bet>
节点及其子节点,而不仅仅是一个 <selection>
子节点?
我想这个问题可以归结为:我可以 select 整个父节点 <bet>
通过子节点的属性值,例如<selection selectionid="[some_value]">
?
[忽略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 将包含 'bet' 注释的所有子项。
// 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());
}
}
DOMXPath
is said to be more robust than SimpleXML
with respect to performance (it has other advantages, e.g. it can properly deal with namespaces). See for example this IBM article 在 PHP.
我很好奇使用 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 所建议的那样将整个文档读入内存。
XML读者可以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()
.