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()函數。
-
當前節點(./):
如./sender表示選擇當前節點下的sender節點集合(等同於下面所講的"特定元素",如:sender) -
父節點(../):
如../sender表示選擇當前節點的父節點下的sender節點集合 -
根元素(/):
如/messages表示選擇從文檔根節點下的messages節點集合. -
根節點(/*):
這里的*是代表所有節點,但是根元素只有一個,所以這里表示根節點。/*的返回結果和/messages返回的結果一樣都是messages節點。 -
遞歸下降(//):
如當前上下文是messages節點。則//sender將返回以下結果:
/messages//sender :
<sender>gkt1980@gmail.com</sender>
<sender>111@gmail.com</sender>
<sender>333@gmail.com</sender>
/messages/message[1]//sender:
<sender>gkt1980@gmail.com</sender>
<sender>111@gmail.com</sender>
我們可以看出XPath表達式返回的結果是:從當前節點開始遞歸步進搜索當前節點下的所有子節點找到滿足條件的節點集。 -
特定元素
如sender:表示選擇當前節點下的sender節點集合,等同於(./sender)
|
軸名稱
|
結果
|
|
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
|
<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>
-
數組下標從0開始(我們知道在XPath查詢表達式中數組下標是從1開始的)
-
不支持在XPath查詢表達式中使用XPath函數。
見:我的另外一篇關於如何使用XSLT的一個小示范 http://www.cnblogs.com/ktgu/archive/2008/12/14/1354890.html
<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>
返回結果
