實用數據綁定: 使用 XPath 作為數據綁定工具,第 1 部分:使用 XPath 選擇 XML 文檔部分
級別: 中級
Brett McLaughlin, 作家,編輯, O'Reilly Media, Inc.
2005 年 12 月 22 日
通 常人們不把 XPath 看作是一種數據綁定 API。除了作為其他規范的一部分以外,XPath 甚至還沒有引起 XML 世界的過多關注。但是只要真正理解了 XPath 是什么以及如何使用它,特別是在 Java? 編程環境中,它就會成為一種強大的數據綁定工具,常常優于傳統的數據綁定 API 如 JAXB 或 JaxMe。Brett McLaughlin 的 “實用數據綁定” 專欄首先用兩期文章探討了作為數據綁定工具的 XPath。
到目前為止,本專欄一直專注于數據綁定的傳統定義和用法:將 XML 文檔轉換成 Java 表示并用于通常的 Java 方法中(比如 getName()
和 setAddress()
)。
然后將 Java 對象再轉換成 XML 表示,通常序列化(保存)到磁盤。我還討論過另一種方向的轉換,即把 Java 對象轉換成 XML,
然后使用轉換的 XML(可以通過網絡連接發送出去,或者作為應用程序中另一個消費 XML
的組件的輸入)。這都是非常有效和實用的數據綁定的用例,但是還沒有包括所有的可能性。本文和下一期文章將介紹另一種方法,即使用 XPath
技術進行數據綁定。
如果您使用數據綁定很長時間了,通常會提供一個約束集(比如 XML 模式或 DTD),用 API 生成表示該約束集的類。然后使用這些類加載 XML 數據,將其中的數據序列化為 XML。這是一種完善的解決方案,但是并非惟一的不使用 SAX 或 DOM 等底層 API 從 XML 文檔中讀取數據的方法。XPath,您可能聽說過這種規范,就是另一種方法。即使您沒有 聽說過 XPath,也可能在某些時候已經用到過它。通過本文,您將了解 XPath 是什么,知道如何使用它,看看它為什么像一種數據綁定 API,甚至可以漂亮地完成其他數據綁定任務很難實現的工作。
XPath 是最常用而不為人知的 XML 技術之一。它實際上是最 常用的 XML 技術 —— XML 轉換必不可少的組成部分。在將來的 Web 中它也起著重要的作用,因為它對 XLink 和(特別是) XPointer 至關重要。甚至 XForms 也暗中要用到它。
那么人們為什么不把 XPath 看作一種獨立的技術呢?很大程度上是因為其他語言中獨立處理 XPath 的 API 還剛剛出現。但是如果使用過 XSLT 或 XPointer,或者處理過更先進的 XML Schema,那么您可能已經走到前面了。
如果使用過 XSLT,幾乎可以肯定曾經看到過 XPath 而且至少在一定程度上看著面熟,即使您并沒有意識到。看一看 清單 1,這是一個非常簡單的 XSL 樣式表的一部分。
清單 1. XSL 樣式表中的 XPath
|
比方說,當您看到 select="address"
或者 select="zipCode"
的時候,您看到的就是應用中的 XPath。包圍在引號中的文本就是 XPath 表達式,盡管非常簡單,要讓樣式表工作卻是不可或缺的。
實際上,即使編寫最簡單的 XML 轉換,如果樣式表中沒有 select
會怎么樣?根本不可能!這是因為 XPath 是 XML 轉換從而也是 XSLT 的完整的一部分。如果您認為自己是一位 XSL 專家,您可能比自己認為的更熟悉 XPath。
XPointer 是另一種大量使用 XPath 的 API,盡管不像 XSL 那么常用,但是也逐漸展露頭角。清單 2 展示了一篇文檔中指向另一篇文檔的鏈接。
清單 2. XPointer 上下文中 XPath 的用法
|
![]() |
|
這是一段稍微復雜的 XPath。它選擇了根元素 cds
中的 cd
元素,該元素有一個 title
屬性等于 August and Everything After
,這些內容都在 cd.xml 文檔中。討論這些似乎有點為時過早,但是先不要擔心語法。關鍵是要看到 XPointer 和 XSL 一樣大量使用了 XPath,事實上沒有它 XPointer 就無法存在。再說一次,XPath 是用于選擇數據的重要組件。
XForms 是一種相對較新的 XML 技術,比不上 XSL 普及,甚至還比不上 XPointer 或 XLink。但是仍然值得提一下,因為它在 input
元素的 ref
屬性中使用 XPath 表達式。可以設置一個 input 并將其綁定到 XML 文檔中的特定元素(或屬性),如 清單 3 所示。
清單 3. XForms 組件使用 XPath 引用綁定到表單的 XML 文檔
|
清單 3 中的 XForms 語句將輸入控件綁定到 XHTML 文檔的特定元素。在這里 XPath 同樣是指定具體元素的關鍵。后面將提到它允許使用一些非常高級的選擇條件。
在很大程度上 XForms 是一種尚未得到支持的技術,但是當使用 XForms 的時候,現在學習的 XPath 是一種很好的工具。將這些關于 XSL、XLink 和 XInclude 的知識結合起來就夠了!
![]() ![]() |
![]()
|
現在您已經確信 XPath 非常有用而且無處不在了,下面開始學習語法吧。如果剛接觸 XPath,本課程將幫助您開始學習和領會 XPath 的結構。如果是一位 XSL 或 XLink 和 XPointer 老手,也可了解為什么 要構造那些奇怪的路徑。您可能已經知道獲取感興趣的數據的其他方法,也許是更好的方法。
首先要認識到稱 XPath 為一種語言有點過于鄭重其事。它實際上是選擇和處理 XML 文檔中的內容的一種語法。即使在 XPath 中使用的函數和運算符也都與選擇有關。在 XPath 中不會創建變量,比方說不可能運行 一個 XPath 程序。沒有這些東西。如果您開始認識到 XPath 是一種巧妙的、有益的選擇 XML 元素和屬性的方法,然后使用選擇的值,您就已經領先于大多數 XML 開發人員了。XPath 不是要對 XML 做 什么事情,而是要從 XML 中取得一些內容。
![]() |
|
對于 XPath 來說第一步是找到一個引用元素的句柄。可是在選擇元素之前需要理解當前上下文。上下文就是在 XML 文檔中的位置。比方說,您可能在根元素上,那根元素就是上下文。當然也可能在第一個 person
元素的第二個 address
元素上。在開始移動和選擇內容之前,需要知道上下文。
只要理解了上下文,就可以掌握 XPath 語法了。以 清單 4 中的 XHTML 文檔為例。
清單 4. Head First Lounge 的 XHTML
|
如果在 清單 4 中的上下文是 html
元素,就可以直接使用名稱選擇一個子元素。比如選擇 body
元素可使用 XPath 表達式 body
。如果想訪問嵌套在 body
中的 h1
元素則使用 body/h1
。如果認為這有點類似于目錄路徑,那么就對了。選擇元素使用 元素名/子元素名
這樣的形式。
不過在前進之前要認識到 XPath 表達式可能返回多個 元素。結果元素集稱為節點集。(事實上,用 XPath 選擇的所有實體 —— 元素、屬性和值 —— 都稱為節點。)比如下面的路徑:
|
清單 4 中 body
有五個不同的 p
子元素,因此那個表達式返回包含五個節點的節點集(正如您所期望的,不 只是第一個 p
元素)。注意,有時候您得到了預期的結果,但是很可能比您打算 要求的多。
還有更多需要注意的地方。現在給出的表達式僅僅返回實際的元素。可能還要獲得這些節點的值。如果需要元素的值(假設元素包含文本數據),需要使用 text()
函數。要獲得第一個 h1
的文本,應使用 body/h1/text()
,對于清單 4 將返回 Our Elixirs
。
當然可以選擇的不僅僅是元素。前面已經提到還可以選擇屬性。選擇屬性的時候要在屬性名前使用 @
符號,其他方面和元素都一樣。
再回到 清單 4 這個例子,可以使用 head/meta/@http-equiv
返回 meta
元素 http-equiv
屬性的值。類似地,head/link/@type
返回 link
元素的 type
屬性。
必須知道,選擇屬性的時候您得到的是它的值,而不像元素那樣返回節點。因此對于屬性選擇器,返回值(雖然不確切但是很有用)是一個值,對于一個元素則是一個節點集(包含一個節點)。
目前為止看到的都是理想的情況,特別是使用文檔的根作為上下文比較簡單。但是顯然情況并非總是如此。這正是目錄結構的真正有用的地方。要從當前上下文移動到上一級,只要使用 ..
運算符。比方說,假設上下文是 body
元素,現在需要獲得網頁的標題(包含在文檔 head
下的 title
元素中),可以使用 ../head/title/text()
。如果您開始感覺到就像是改變 UNIX 或 Mac OS X 終端上的目錄,這就對了!
如果上下文是 body
中第二個 p
的 img
元素,要得到標題該怎么辦呢?可以使用 ../../../head/title/text()
。但是數這些 ../
很快就變得單調乏味了。因此能直接跳到根元素就好了,再想一想 UNIX,使用 /
返回到根元素就毫不奇怪了。同樣的選擇器更簡單的形式為 /html/head/title/text()
。雖然沒有縮短,但是更清楚了。有了 ../
和 /
之后,基本上就能移動到需要的任何地方了。
考慮到使用一個 XPath 表達式可以選擇多個節點,事情就更有趣了。您可以使用對應多個節點的簡單表達式,比如 /html/body/h2
來進行多重選擇。但是也可使用通配符 進一步精化這些選擇。XPath 中有三個統配符:
-
*
與任何元素匹配,無論元素名是什么 -
node()
與所有的節點類型匹配(元素、文本節點、注釋、屬性,等等) -
@*
與所有的屬性節點匹配,無論屬性名是什么
比如,可以使用 /html/body/*
選擇 body
元素的所有直接子元素。也可用 /html/head/meta/@*
選擇 meta
標記的所有屬性。
所有這些情況下,要記住您得到的是一個節點集,因此不應該滿足于處理某個值就結束(除非您已經選擇了一個屬性,稍后要講到)。但是,只要使用方法、函數或模板處理多個節點,這些技術就很重要。
![]() ![]() |
![]()
|
基本的東西很好,但有時候還需要點特別的讓您身邊的同事眼花繚亂的東西,或者需要一點特殊的 功能。這種情況下這些基礎可能就不夠了。雖然很難巨細無遺地介紹 XPath,但下面給出一些高級技巧,可以幫助您在 XPath 應用程序中取得需要的節點(或節點集)。
到目前為止,看到的路徑都是選擇一個節點,并假設您知道該節點在何處。比方說,您知道 title
、img
或 p
在文檔中什么位置,只需要導航到這些元素。但有時候可能希望打破這些結構,僅僅擷取某種元素而不管其位置(或者無論給定的起始上下文的位置)。為此可使用后代選擇器。
后代選擇器用雙斜線 //
表示。使用它告訴 XPath 選擇所有指定的節點,無論嵌套得多深。比如這個簡單的特定于 XHTML 的 XPath:
|
XPath 選擇嵌套在 XHTML 的 body
元素中的所有table
元素,不論直接嵌套在 body
中(如 /html/body/table
),還是嵌套了多層(如 /html/body/table/tr/td/table
)。這種情況下,嵌套的 table
和頂層 table
同時被選中。
![]() |
|
當與屬性結合使用的時候就變得很有趣了(使用 @
)。比方說,假設要選擇具有 id
屬性的所有元素。可使用 //@id
,首先跳回根元素然后選擇文檔中的所有 id
屬性。但是實際上要訪問的是元素而不是屬性,因此需要從屬性上移一層到包含這些屬性的元素:
|
您應該嘗試結合這些不同的方法,會看到一些很有意思的結果。
假設有一個條件希望作為匹配的基礎,比如需要 class
屬性值為 greentea
的 p
元素。如果知道如何使用方括號,這一點在 XPath 中很容易做到。下面是一個例子:
|
方括號用于指定條件,可使用 =
甚至 <
和 >
。也許要用一個范圍更廣的表達式:
|
明白了嗎?甚至還能更進一步,選擇屬于 greentea
類的所有 元素而不論其元素類型是什么:
|
這個表達式可能看起來有點奇怪,但很容易解釋。首先,//
意味著從根元素開始選擇所有的元素(與后面的選擇器及條件匹配)而不論其嵌套的位置。然后 @class
選擇文檔中所有的 class
屬性。后面的 [.="greentea"]
有點神秘。="greentea"
部分容易理解,它用 greentea
匹配等式左側的值。在這里就是 .
,這個標志還沒有見到過。但是再想一想目錄,..
選擇父節點(或者父目錄),.
則選擇當前節點。因此 //@class[.="greentea"]
選擇值為 greentea
的所有 class
屬性。然后再移動到這些屬性所在的元素:
|
現在看起來有點不可思議,但是應該熟悉這類奇怪的表達式。當需要訪問特定的元素、屬性或節點集時它們很有用。
隨著越來越多的使用屬性選擇器,最終將與處理節點一樣經常地使用值(來自屬性)。如果處理過非常典型的 XML,一定會遇到數字。XML 文檔常常把數字值放在屬性中(有時候在元素中)。因此這一節討論 /people/person[firstname = "John"]/@born
和 /people/person/numChildren/text()
這類表達式的結果(當然,使用元素表示子女的個數不夠典型,不過就用下面的例子吧)。
這時候您會發現 XPath 的計算能力很有用。可以像其他編程語言中那樣使用 +
、-
和 *
。此外 div
用于除法,mod
則表示求模(兩數相除的余數)。比如,假設 XML 文檔中包含用四位數表示的出生年份,現在需要改成兩位數的形式,首先用下面的表達式取得實際的出生年份:
|
這樣就得到了出生年份(可能是 1976 或者 1945)。然后只需要減去 1900:
|
當然這種方法很有限,新千年出生的孩子和那些歷史人物都會造成這一公式的失敗。因此應該用 mod
:
|
(順便說一下,應該告訴那些研究 Y2K 的人您省略了年份的前兩位數。)
最后,XPath 還提供了一些很棒的字符串處理功能。XML 基本上都是文本,屬性值和元素中的數據通常是文本,因此不用奇怪 XPath 支持某些字符串操作。下面僅僅是 XPath 提供的處理字符串的少數函數:
-
string()
將數據轉換成字符串格式,如果還不是字符串的話。 -
starts-with(full-string, start-string)
返回一個 Boolean 值,檢查full-string
是否以start-string
開始。 -
contains(full-string, contains-string)
返回一個 boolean 值,檢查full-string
是否包含contains-string
。 -
string-length(string)
返回string
的長度。 -
normalize-space(string)
去掉string
兩端和內部的空格。
大部分函數的功能都很明確。starts-with("McLaughlin", "Mc")
返回 true
,contains("McLaughlin", "augh")
同樣如此。string-length("Brett")
顯然返回 5
,normalize-space(" Brett McLaughlin ")
則返回 "Brett McLaughlin"
。是不是很簡單?當然這些可用于所有 XPath 表達式的返回值,比如 /html/body/p[class='greentea']
。因此要獲取 清單 4 中 p
的文本可用 normalize-space()
:
|
更妙的是可用 string()
取出多個元素的文本。比如,如果希望取得 清單 4 所示 XHTML 中所有p
元素的所有 文本,可用:
|
分析最后一個例子可以了解很多內容:
-
//p
選擇文檔中所有的p
元素,無論其位于何處。 -
string(//p)
獲取這些元素的內容組成一個大字符串。但是字符串中包含很多無用的空白,因此還要進一步處理。 -
normalize-space(string(//p))
規范化內容中的空白字符,得到您希望的文本。
![]() ![]() |
![]()
|
現
在您可能已經忘記正在閱讀的是一篇關于數據綁定的文章。但是先不要忙于跟過去幾年中您讀過的其他 X* 文檔和 API
比較。您讀到的實際上是另一種數據綁定方法。考慮這樣一種觀點,數據綁定是在 Java 代碼中保留 XML
文檔邏輯(或者語義,如果您喜歡的話)意義。如果 XML 文檔中的 address
元素包含 street
、city
和 state
子元素,想要用 getAddress().getStreet()
這樣的形式得到這些元素的值,比如:
|
這就是某種基本的數據綁定形式。但是在 XPath 中也能做到同樣的事!可以利用 address/street/text()
或者 /person[last-name="Gosling"]/address[@type="work"]/city
這樣的 XPath 表達式來得到街道名。初看起來與前一個例子不同,但是它仍然合理地使用了 XML 數據,您要的是 person、address 和 street 而不是第一個子元素、第二個文本節點 或者屬性。這一點至關重要。XPath 按照邏輯而不是結構來處理 XML。基本上數據綁定也是如此,按照邏輯處理數據,而不用擔心其結構。
為了避免誤導,需要指出使用 XPath 仍然需要對結構有所了解。比如,@
運算符只用于屬性,因此必須知道 type
(地址的一部分)用元素還是屬性表示。在傳統的數據綁定中,只需要調用 getAddress().getType()
而不需要管結構的層次。但是,這點小小的代價是值得的,因為不需要處理大量的生成類、額外的類路徑問題、等待封送和解除封送處理以及傳統數據綁定的其他缺點。
只 剩下這個等式中需要在 Java 語言中增加的部分:接受 XML 文檔和 XPath 表達式,以 Java 友好的方式獲得表達式的結果。這些內容將在第 2 部分介紹,很快就要把 XPath 與 Java 編程中其他以 XML 為中心的工具一起使用了。很多情況下您會發現,與使用生成類和 JAXB 這樣的 API 的工具相比,XPath 是一種更好的 數據綁定工具。
![]() ![]() |
![]()
|
-
參與論壇討論。
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
-
XML Path Language (XPath)
Version 1.0:閱讀 W3C 推薦標準。
-
XML Path Language (XPath) 2.0:這是 XPath 1.0 的超集,增加了更豐富的數據類型支持,使用 XML Schema 驗證文檔時可使用這些類型信息。
- “用于數據的 XML: XPath 2.0 有哪些新特性?”(developerWorks,2002 年 9 月):了解 XPath 1.x 和 2.0 的區別。
- “XPath 入門”(developerWorks,2004 年 3 月):需要了解或復習 XPath,可以閱讀 Bertrand Portier 撰寫的這篇入門教程。
-
The Extensible Stylesheet Language Family (XSL):XPath 是 XSL 和 XSLT 的重要部分。進一步了解 W3C 的 XSL 規范家族,包括 XSL Transformations (XSLT) Version 1.0。
-
XQuery 1.0 and XPath 2.0 Data Model (XDM):伴隨著 XPath 2.0,XQuery 希望定義能同時用于兩種規范的數據模型。
- “Toward an XPath API”:通過 Leigh Dodds 的這篇文章了解導致最初的 1.0 XPath API 誕生的原因。
-
Java and XML
:Brett McLaughlin 撰寫的這本書的第三版(O'Reilly Media, Inc.)專門用一章討論 XPath。
-
XML in a Nutshell
:Elliotte Rusty Harold 和 W. Scott Means 撰寫的這部 XML 資料大全(O'Reilly Media, Inc.)專門有一章討論 XPath。
-
developerWorks XML 專區:可以找到更多 XML 資源,包括文章、教程、技巧和標準。
-
developerWorks Java 技術專區:數百篇文章、教程和技巧可以幫助您掌握大部分 Java 語言技術和相關應用。
- IBM Certified Solution Developer —— XML 和相關技術:了解如何通過認證。
![]() ![]() |
![]()
|
![]() |
||
|
![]() |
Brett McLaughlin 從 Logo 時代就開始使用計算機。(還記得那個小三角嗎?)近年來他已經成為 Java 技術和 XML 社區最知名的作家和程序員之一。他曾經在 Nextel Communications 實現過復雜的企業系統,在 Lutris Technologies 編寫應用程序服務器,最近在 O'Reilly Media, Inc. 繼續撰寫和編輯這方面的圖書。在他的新書 Head Rush Ajax 中,Brett 與暢銷書作家 Eric 及 Beth Freeman 為 Ajax 帶來了獲獎的創新方法 Head First。最近的著作 Java 1.5 Tiger: A Developer's Notebook 是關于這一 Java 技術新版本的第一本書籍,經典著作 Java and XML 仍然是在 Java 語言中使用 XML 技術的權威圖書。 |
posted on 2006-03-18 20:47 Vincent.Chen 閱讀(594) 評論(0) 編輯 收藏 所屬分類: AJAX