[英]The same XPaths - different results
$str = '
<body>
<table><tr><td><b class="1">1</b></td></tr></table>
<table><tr><td><b class="2">1</b></td></tr></table>
<p>some text</p>
</body>';
$dom = new DOMDocument();
$dom->loadHTML($str);
$xpath = new DOMXpath($dom);
foreach($xpath->query('//table[//b[contains(@class, "2")]]') as $i)
print_r($i);
echo "------------------------------------------\n";
foreach($xpath->query('//table//b[contains(@class, "2")]/ancestor::table') as $i)
print_r($i);
第一个XPath选择两个表,而第二个仅获取目标(第二个)表。 为什么?
接受的答案可以纠正错误,但不能真正解释原始路径表达式为什么出错。
您的第一个表达式如下:
//table[//b[contains(@class, "2")]]
它有两个谓词 ,一个嵌套在另一个中:
//table[//b[contains(@class, "2")]]
^---------------------^ inner predicate
^--------------------------^ outer predicate
将谓词视为应用于谓词左侧上下文的过滤器。 在极端情况下,此类谓词不会丢弃任何中间结果节点,也不会丢弃所有中间结果节点。
每个中间结果节点只有在其右边的谓词评估为true
才会保留。 对于内部谓词:
//b[contains(@class, "2")]
//b
产生一组中间的b
元素节点(整个文档中的所有b
元素节点),然后由谓词[contains(@class, "2")]
过滤。 给定您输入的XML文档,谓词内部的表达式仅对b
元素之一返回true
。
但是//b[contains(@class, "2")]
依次充当外部谓词的内容:
//table[outer predicate]
现在, //table
选择整个文档中所有table
元素节点作为中间结果,并对每个谓词检查谓词内部的表达式。
重要的是,外部谓词//b[contains(@class, "2")]
对于两个 table
元素都将返回true
。 这是因为对于他们两个来说,在整个文档中某处确实存在一个b
元素,其class
属性包含2
。
您真正想做的是:从每个table
元素的角度评估外部谓词表达式-接受的答案显示了如何执行此操作。 即,在谓词中使用.//
代替//
。
XPath谓词[//b...]
有一个错误。 它应该是[.//b...]
。
说明: [...]
是谓词,它们仅充当过滤器。 说a[b]
,选择所有满足谓词[b]
a
节点。 如果a
和b
是元素,它会,从当前上下文节点,选择所有a
包含一个元素b
元件作为子元素。
//b
是AbbreviatedAbsoluteLocationPath
并选择整个文档中的所有b
元素节点。 两个表都在带有b
元素的文档中,因此谓词[//b]
对于您的文档始终为true,无论您在哪里应用它。 .//b
是AbbreviatedRelativeLocationPath
并选择作为后代的所有b
元素节点(子代及其子代,递归)。 谓词[.//b]
仅适用于具有后代元素b
table
元素。 如果步骤路径表达式选择的节点集不为空,则像//b
或.//b
这样的步骤路径表达式在用作[//b]
或[.//b]
类的谓词时为true 。
由于//b
而不是.//b
,因此应用的谓词没有任何改变: //b[contains(@class, "2")]
选择整个文档中所有包含“ 2”的元素在其class
属性中。 您基本上是在检查文档,而不是对所需table
元素下方的树进行检查,并且两个table
元素都满足该文档检查的要求,因为这两个检查都在文档中,该文档的class
属性中包含一个b
元素带有“ 2”的元素。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.