相同的 XPaths - 不同的结果

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,而第二个只选择目标(第二个)table。为什么?

test on eval.in

您的 XPath 谓词 [//b...] 中存在错误。它应该是 [.//b...] 而不是。

说明: [...] 是谓词,它们仅充当过滤器。当你说 a[b] 时,你 select 满足谓词 [b] 的所有 a 个节点。如果 ab 是元素,它将从当前上下文节点 select 所有包含 b 元素的 a 元素作为 child元素.

  • //b是整个文档中的一个AbbreviatedAbsoluteLocationPath和select的所有b元素节点。两个表都在具有符合条件的 b 元素的文档中,因此谓词 [//b] 对于您的文档始终为真,无论您在何处应用它。
  • .//b 是一个 AbbreviatedRelativeLocationPath 和 select 的所有 b 元素节点,它们是后代(children 和它们的 children ,递归)。谓词 [.//b] 仅对具有后代元素 b.
  • table 元素为真

步骤路径表达式,如 //b.//b,当用作谓词如 [//b][.//b] 时,是 true如果由步骤路径表达式编辑的节点集 select 不为空。

应用的谓词不会改变任何关于它的内容,因为 //b 而不是 .//b//b[contains(@class, "2")] select 都是整个文档中在 class 属性中包含“2”的元素。您基本上是在对文档执行检查,而不是在所需的 table 元素下方的树上执行检查,并且两个 table 元素都满足该文档检查,因为它们都在包含 [=17 的文档中=] 在其 class 属性中具有“2”的元素。

接受的答案纠正了错误,但并没有真正解释原始路径表达式出错的原因。

您的第一个表达式如下:

//table[//b[contains(@class, "2")]]

它有两个 谓词,一个嵌套在另一个里面:

//table[//b[contains(@class, "2")]]
           ^---------------------^       inner predicate
       ^--------------------------^      outer predicate

将谓词视为应用于谓词左侧上下文的过滤器。在极端情况下,none 或所有中间结果节点都被此类谓词丢弃。

仅当其右侧的谓词计算结果为 true 时,才会保留每个中间结果节点。对于内部谓词:

//b[contains(@class, "2")]

//b 产生一组中间 b 元素节点(整个文档中的所有 b 元素节点),然后由谓词 [contains(@class, "2")] 过滤。给定您输入的 XML 文档,谓词中的表达式仅 returns true for one of the b elements.

但是//b[contains(@class, "2")]又作为外谓词的内容:

//table[outer predicate]

现在 //table 选择整个文档中的所有 table 个元素节点作为中间结果,并为每个节点检查谓词中的表达式。

重要的是,外部谓词 //b[contains(@class, "2")] 将 return true 用于 both table 元素。这是因为对于他们两个来说,在整个文档的某处确实有一个 b 元素,其 class 属性包含 2.

您真正想要做的是:从每个 table 元素的角度评估外部谓词表达式 - 接受的答案显示了如何做到这一点。即,在谓词中使用 .// 而不是 //