[英]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.