XPath詳解,總結
經常在工作中會使用到XPath的相關知識,但每次總會在一些關鍵的地方不記得或不太清楚,所以免不了每次總要查一些零碎的知識,感覺即很煩又浪費時間,所以對XPath歸納及總結一下。
在這篇文章中你將能學習到:
- XPath簡介
- XPath 路徑表示式詳解
- XPath在DOM,XSLT及XQuery中的應用
XPath簡介
XPath是W3C的一個標準。它最主要的目的是為了在XML1.0或XML1.1文件節點樹中定位節點所設計。目前有XPath1.0和XPath2.0兩個版本。其中Xpath1.0是1999年成為W3C標準,而XPath2.0標準的確立是在2007年。W3C關於XPath的英文詳細文件請見:http://www.w3.org/TR/xpath20/
XPath是一種表示式語言,它的返回值可能是節點,節點集合,原子值,以及節點和原子值的混合等。XPath2.0是XPath1.0的超集。它是對XPath1.0的擴充套件,它可以支援更加豐富的資料型別,並且XPath2.0保持了對XPath1.0的相對很好的向後相容性,幾乎所有的XPath2.0的返回結果都可以和XPath1.0保持一樣。另外XPath2.0也是XSLT2.0和XQuery1.0的用於查詢定位節點的主表示式語言。XQuery1.0是對XPath2.0的擴充套件。關於在XSLT和XQuery中使用XPath表示式定位節點的知識在後面的例項中會有所介紹。
在學習XPath之前你應該對XML的節點,元素,屬性,原子值(文字),處理指令,註釋,根節點(文件節點),名稱空間以及對節點間的關係如:父(Parent),子(Children),兄弟(Sibling),先輩(Ancestor),後代(Descendant)等概念有所瞭解。這裡不在說明。
XPath路徑表示式
在本小節下面的內容中你將可以學習到:
- 路徑表示式語法
- 相對/絕對路徑
- 表示式上下文
- 謂詞(篩選表示式)及軸的概念
- 運算子及特殊字元
- 常用表示式例項
- 函式及說明
這裡給出一個例項Xml檔案。下面的說明及例項都是基於該XML檔案。
-
路徑表示式語法:
- 路徑 = 相對路徑 | 絕對路徑
- XPath路徑表示式 = 步進表示式 | 相對路徑 "/"步進表示式。
- 步進表示式=軸 節點測試 謂詞
說明:
- 其中軸表示步進表示式選擇的節點和當前上下文節點間的樹狀關係(層次關係),節點測試指定步進表示式選擇的節點名稱副檔名,謂詞即相當於過濾表示式以進一步過濾細化節點集。
- 謂詞可以是0個或多個。多個多個謂詞用邏輯操作符and, or連線。取邏輯非用not()函式。
請看一個典型的XPath查詢表示式:/messages/message//child::node()[@id=0],其中/messages/message是路徑(絕對路徑以"/"開始),child::是軸表示在子節點下選擇,node()是節點測試表示選擇所有的節點。[@id=0]是謂詞,表示選擇所有有屬性id並且值為0的節點。
- 相對路徑與絕對路徑:
如果"/"處在XPath表示式開頭則表示文件根元素,(表示式中間作為分隔符用以分割每一個步進表示式)如:/messages/message/subject是一種絕對路徑表示法,它表明是從文件根開始查詢節點。假設當前節點是在第一個message節點【/messages/message[1]】,則路徑表示式subject(路徑前沒有"/")這種表示法稱為相對路徑,表明從當前節點開始查詢。具體請見下面所述的"表示式上下文"。
- 表示式上下文(Context):
上下文其實表示一種環境。以明確當前XPath路徑表示式處在什麼樣的環境下執行。例如同樣一個路徑表示式處在對根節點操作的環境和處在對某一個特定子節點操作的環境下執行所獲得的結果可能是完全不一樣的。也就是說XPath路徑表示式計算結果取決於它所處的上下文。
XPath上下文基本有以下幾種:
-
當前節點(./):
如./sender表示選擇當前節點下的sender節點集合(等同於下面所講的"特定元素",如:sender)
-
父節點(../):
如../sender表示選擇當前節點的父節點下的sender節點集合
-
根元素(/):
如/messages表示選擇從文件根節點下的messages節點集合.
-
根節點(/*):
這裡的*是代表所有節點,但是根元素只有一個,所以這裡表示根節點。/*的返回結果和/messages返回的結果一樣都是messages節點。
-
遞迴下降(//):
如當前上下文是messages節點。則//sender將返回以下結果:
/messages//sender :
<sender>[email protected]</sender>
<sender>[email protected]</sender>
<sender>[email protected]</sender>
/messages/message[1]//sender:
<sender>[email protected]</sender>
<sender>[email protected]</sender>
我們可以看出XPath表示式返回的結果是:從當前節點開始遞迴步進搜尋當前節點下的所有子節點找到滿足條件的節點集。
-
特定元素
如sender:表示選擇當前節點下的sender節點集合,等同於(./sender)
- 謂詞(篩選表示式)及軸的概念:
XPath的謂詞即篩選表示式,類似於SQL的where子句.
軸名稱 |
結果 |
ancestor |
選取當前節點的所有先輩(父、祖父等) |
ancestor-or-self |
選取當前節點的所有先輩(父、祖父等)以及當前節點本身 |
attribute |
選取當前節點的所有屬性 |
child |
選取當前節點的所有子元素。 |
descendant |
選取當前節點的所有後代元素(子、孫等)。 |
descendant-or-self |
選取當前節點的所有後代元素(子、孫等)以及當前節點本身。 |
following |
選取文件中當前節點的結束標籤之後的所有節點。 |
namespace |
選取當前節點的所有名稱空間節點 |
parent |
選取當前節點的父節點。 |
preceding |
直到所有這個節點的父輩節點,順序選擇每個父輩節點前的所有同級節點 |
preceding-sibling |
選取當前節點之前的所有同級節點。 |
self |
選取當前節點。 |
- 運算子及特殊字元:
運算子/特殊字元 |
說明 |
/ |
此路徑運算子出現在模式開頭時,表示應從根節點選擇。 |
// |
從當前節點開始遞迴下降,此路徑運算子出現在模式開頭時,表示應從根節點遞迴下降。 |
. |
當前上下文。 |
.. |
當前上下文節點父級。 |
* |
萬用字元;選擇所有元素節點與元素名無關。(不包括文字,註釋,指令等節點,如果也要包含這些節點請用node()函式) |
@ |
屬性名的字首。 |
@* |
選擇所有屬性,與名稱無關。 |
: |
名稱空間分隔符;將名稱空間字首與元素名或屬性名分隔。 |
( ) |
括號運算子(優先順序最高),強制運算優先順序。 |
[ ] |
應用篩選模式(即謂詞,包括"過濾表示式"和"軸(向前/向後)")。 |
[ ] |
下標運算子;用於在集合中編制索引。 |
| |
兩個節點集合的聯合,如://messages/message/to | //messages/message/cc |
- |
減法。 |
div, |
浮點除法。 |
and, or |
邏輯運算。 |
mod |
求餘。 |
not() |
邏輯非 |
= |
等於 |
!= |
不等於 |
特殊比較運算子 |
< 或者 < <= 或者 <= > 或者 > >= 或者 >= 需要轉義的時候必須使用轉義的形式,如在XSLT中,而在XMLDOM的scripting中不需要轉義。 |
- 常用表示式例項:
/ |
Document Root文件根. |
/* |
選擇文件根下面的所有元素節點,即根節點(XML文件只有一個根節點) |
/node() |
根元素下所有的節點(包括文字節點,註釋節點等) |
/text() |
查詢文件根節點下的所有文字節點 |
/messages/message |
messages節點下的所有message節點 |
/messages/message[1] |
messages節點下的第一個message節點 |
/messages/message[1]/self::node() |
第一個message節點(self軸表示自身,node()表示選擇所有節點) |
/messages/message[1]/node() |
第一個message節點下的所有子節點 |
/messages/message[1]/*[last()] |
第一個message節點的最後一個子節點 |
/messages/message[1]/[last()] |
Error,謂詞前必須是節點或節點集 |
/messages/message[1]/node()[last()] |
第一個message節點的最後一個子節點 |
/messages/message[1]/text() |
第一個message節點的所有子節點 |
/messages/message[1]//text() |
第一個message節點下遞迴下降查詢所有的文字節點(無限深度) |
/messages/message[1] /child::node() /messages/message[1] /node() /messages/message[position()=1]/node() //message[@id=1] /node() |
第一個message節點下的所有子節點 |
//message[@id=1] //child::node() |
遞迴所有子節點(無限深度) |
//message[position()=1]/node() |
選擇id=1的message節點以及id=0的message節點 |
/messages/message[1] /parent::* |
Messages節點 |
/messages/message[1]/body/attachments/parent::node() /messages/message[1]/body/attachments/parent::* /messages/message[1]/body/attachments/.. |
attachments節點的父節點。父節點只有一個,所以node()和* 返回結果一樣。 (..也表示父節點. 表示自身節點) |
//message[@id=0]/ancestor::* |
Ancestor軸表示所有的祖輩,父,祖父等。 向上遞迴 |
//message[@id=0]/ancestor-or-self::* |
向上遞迴,包含自身 |
//message[@id=0]/ancestor::node() |
對比使用*,多一個文件根元素(Document root) |
/messages/message[1]/descendant::node() //messages/message[1]//node() |
遞迴下降查詢message節點的所有節點 |
/messages/message[1]/sender/following::* |
查詢第一個message節點的sender節點後的所有同級節點,並對每一個同級節點遞歸向下查詢。 |
//message[@id=1]/sender/following-sibling::* |
查詢id=1的message節點的sender節點的所有後續的同級節點。 |
//message[@id=1]/datetime/@date |
查詢id=1的message節點的datetime節點的date屬性 |
//message[@id=1]/datetime[@date] //message/datetime[attribute::date] |
查詢id=1的message節點的所有含有date屬性的datetime節點 |
//message[datetime] |
查詢所有含有datetime節點的message節點 |
//message/datetime/attribute::* //message/datetime/attribute::node() //message/datetime/@* |
返回message節點下datetime節點的所有屬性節點 |
//message/datetime[attribute::*] //message/datetime[attribute::node()] //message/datetime[@*] //message/datetime[@node()] |
選擇所有含有屬性的datetime節點 |
//attribute::* |
選擇根節點下的所有屬性節點 |
//message[@id=0]/body/preceding::node() |
順序選擇body節點所在節點前的所有同級節點。(查詢順序為:先找到body節點的頂級節點(根節點),得到根節點標籤前的所有同級節點,執行完成後繼續向下一級,順序得到該節點標籤前的所有同級節點,依次類推。) 注意:查詢同級節點是順序查詢,而不是遞迴查詢。 |
//message[@id=0]/body/preceding-sibling::node() |
順序查詢body標籤前的所有同級節點。(和上例一個最大的區別是:不從最頂層開始到body節點逐層查詢。我們可以理解成少了一個迴圈,而只查詢當前節點前的同級節點) |
//message[@id=1]//*[namespace::amazon] |
查詢id=1的所有message節點下的所有名稱空間為amazon的節點。 |
//namespace::* |
文件中的所有的名稱空間節點。(包括預設名稱空間xmlns:xml) |
//message[@id=0]//books/*[local-name()='book'] |
選擇books下的所有的book節點, 注意:由於book節點定義了名稱空間<amazone:book>.若寫成//message[@id=0]//books/book則查找不出任何節點。 |
//message[@id=0]//books/*[local-name()='book' and namespace-uri()='http://www.amazon.com/books/schema'] |
選擇books下的所有的book節點,(節點名和名稱空間都匹配) |
//message[@id=0]//books/*[local-name()='book'][year>2006] |
選擇year節點值>2006的book節點 |
//message[@id=0]//books/*[local-name()='book'][1]/year>2006 |
指示第一個book節點的year節點值是否大於2006. 返回xs:boolean: true |
- 函式及說明:
XPath在DOM,XSLT及XQuery中的應用
DOM:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>XPath Test</title> </head> <body> <script language="javascript" type="text/javascript"> var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async="false"; xmlDoc.load("messages.xml"); xmlDoc.setProperty("SelectionLanguage", "XPath"); var sPath = "/messages/message[1]//books/*[local-name()='book']"; var bookNodes = xmlDoc.selectNodes(sPath); document.write("<ul>"); for ( var i = 0; i < bookNodes.length; i++) { document.write("<li>" + bookNodes[i].childNodes[0].text + "</li>"); } document.write("</ul>"); </script> </body> </html> |
注意:
我們若使用new ActiveXObject("Microsoft.XMLDOM")則需要注意的是:因為早期的XMLDOM的SelectionLanguage屬性預設是正則表示式,不是XPath語言。所以需要指定這樣一條語句xmlDoc.setProperty("SelectionLanguage", "XPath"); 以支援XPath查詢表示式。.
若沒有指定SelectionLanguage屬性值為XPath則要注意以下情況:
- 陣列下標從0開始(我們知道在XPath查詢表示式中陣列下標是從1開始的)
- 不支援在XPath查詢表示式中使用XPath函式。
XSLT:
XQuery:
XQuery查詢表示式:
xquery version "1.0"; <ul> { let $i := 0 for $x in doc("C:\Users\Administrator\Desktop\messages.xml")//message[@id=0]//books/*[local-name()='book'] where $x/year>2006 order by $x/year descending return <li>{ data($x/name) } </li> } </ul> |
返回結果: