【问题标题】:php xpath with text() and SimpleXMLElement->xpath not in line with xpath expected results带有 text() 和 SimpleXMLElement->xpath 的 php xpath 不符合 xpath 预期结果
【发布时间】:2013-11-07 21:53:43
【问题描述】:

我正在尝试获取 /td/span 的所有文本节点。

我正在尝试使用 xpath /td/span/text()

问题是它返回每个文本元素的所有文本节点(这里有两个,“193”和“120”,它返回两次“193120”,而不是在单独的元素中返回 193 和 120)。

我在任何在线工具上尝试完全相同的 xpath,它工作正常,在 php 中,完全不同的结果。

使用 SimpleXMLElement

$xhtmlSnippet = '<td><span>193<span>10</span><span></span><div>66</div><span>195</span><span>.</span><span>34</span><span>242</span><span></span>120<span>64</span></span></td>';

$xml = new SimpleXMLElement($xhtmlSnippet);

$xresult = $xml->xpath('/td/span/text()');    

foreach($xresult as $xnode){
    echo "<br /><br />NodeValue: " . $xnode;
}

给我:

节点值:193120

节点值:193120

这是一个通过在线工具正常工作的示例(所有其他在线工具也提供预期的输出):

Working example in online tester

编辑:

使用 DOMDocument + DOMXPath,它似​​乎按预期工作:

    $dom = new DOMDocument;
    $dom->loadXML($xhtmlSnippet);

    $xpath = new DOMXPath($dom);
    
    foreach ($xpath->query('/td/span/text()) as $textNode) {
        echo "\n\nTextNode: " . $textNode->nodeValue;
    }

给予:

文本节点:193

文本节点:120

【问题讨论】:

  • 是的,我刚刚更改为 DOMDocument ......但我不知道为什么 SimpleXMLElement 没有按预期工作......任何线索?
  • 除了 SimpleXMLElement 出错并不奇怪,不,我恐怕不明白那里发生了什么。

标签: php xpath simplexml


【解决方案1】:

SimpleXMLElement 只能表示元素和属性,可以是单独的,也可以是同一类型的兄弟的集合。 -&gt;xpath() method 返回一个 SimpleXMLElement 对象数组,这允许它们是非兄弟节点,但不允许任何其他节点类型。

因此,表达式 /td/span/text() 匹配两个文本节点,但将它们作为表示其父元素的对象返回,在这种情况下恰好是相同的 &lt;span&gt; 元素,从而两次为您提供具有相同对象的数组.

难题的其余部分是,当您将 SimpleXML 元素转换为字符串时,它会将其所有直接后代文本和 CDATA 节点组合成一个字符串,因此 193120 会粘在一起。

因此输出是193120,两次。

(这绝对是不直观的行为,尽管很难完全知道 SimpleXML 在这种情况下应该做什么;如果 XPath 表达式解析为元素或属性以外​​的其他内容,那么产生错误可能会更好)。


由于 DOM API 对可能存在于 XML 中的每一种节点都有对象,而且 PHP 包含该 API 的完整实现,因此 XPath 表达式将在那里按预期工作。更重要的是,SimpleXML 和 DOM 对象实际上都是围绕相同内部内存结构的包装器,因此您可以使用 dom_import_simplexml()simplexml_import_dom() 编写将两者结合起来的操作。

举个不太优雅的例子,如果您想在已经使用 SimpleXML 遍历的元素的上下文中运行 XPath 表达式,您可以执行以下操作:

$dom_node = dom_import_simplexml($simplexml_node);
$dom_xpath = new DOMXPath($dom_node->ownerDocument);
$dom_xpath_result = $dom_xpath->query('span/text()', $dom_node);

foreach($dom_xpath_result as $xnode){
    echo "<br /><br />NodeValue: " . $xnode->nodeValue;
}

显然,您可以根据需要将其包装成一个函数。另请注意,由于您的表达式从文档根(前导 /)开始,实际上下文无关紧要,这就是我在上面使用稍微不同的表达式的原因。

【讨论】:

  • 是的,使用 DOMDocument 的 xpath 不会表现出同样的行为!非常令人沮丧,但我想 xml 中没有裸文本元素,所有内容都有父元素,并且任何裸文本都在树的末尾。
  • DOM 为各种不同的节点类型(包括文本节点)提供了单独的类,因此可以“正确地”表示这个 XPath 表达式的“答案”。但是,您是否意识到可以在 PHP 中混合使用这两种 API?我将在我的答案中编辑一个示例。
猜你喜欢
  • 2018-09-01
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多