相同的 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。为什么?
您的 XPath 谓词 [//b...]
中存在错误。它应该是 [.//b...]
而不是。
说明:
[...]
是谓词,它们仅充当过滤器。当你说 a[b]
时,你 select 满足谓词 [b]
的所有 a
个节点。如果 a
和 b
是元素,它将从当前上下文节点 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
元素的角度评估外部谓词表达式 - 接受的答案显示了如何做到这一点。即,在谓词中使用 .//
而不是 //
。
$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。为什么?
您的 XPath 谓词 [//b...]
中存在错误。它应该是 [.//b...]
而不是。
说明:
[...]
是谓词,它们仅充当过滤器。当你说 a[b]
时,你 select 满足谓词 [b]
的所有 a
个节点。如果 a
和 b
是元素,它将从当前上下文节点 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
元素的角度评估外部谓词表达式 - 接受的答案显示了如何做到这一点。即,在谓词中使用 .//
而不是 //
。