XPath 2.0 的新特性 (轉)

worldblog發表於2007-12-12
XPath 2.0 的新特性 (轉)[@more@] words" content=", XQuery, ">

XPath 2.0 的新特性

關鍵字: XPath 2, XQuery, XML
原作:2002.4.20, to:fwjsoft@.com.cn">onestab 譯自

本文將帶你領略一下XPath2.0的一些新特性。假定你已經對XPath1.0有了初步瞭解,並且經常在T環境下使用它。在此並不準備對它作詳盡的描述,只是點出一些最值得關注的特性。

XPath1.0 和 XPath2.0的相互關係

和 中都這樣說:“XPath是用於對XML的部分進行定址(addressing)的語言”。這是對XPath 1.0特性的恰當描述。(當然,這裡沒有提到你可以使用算術和字串、數字和布林表示式,但這些特性被儘可能地減到最少。)另一方面,人們對XPath2.0的特性所寄予的期望更多。XPath2.0是一個非常強大的語言,可操作的資料型別範圍更廣。對XPath2.0的較好的描述是: 它是一個處理序列的表示式語言,並內在地支援XML檔案的查詢。查詢?這不是XQuery的任務嗎?

XPath2.0 和 XQuery1.0的相互關係

一年多以來,的XSL 和XML Query 工作組就一直在一起工作,目標是在XSL2.0和XQuery1.0之間有儘可能多的共享內容,並且在技術和原則上可行,最後把這兩者之間的共同部分命名為"XPath 2.0"。這就意味著XPath2.0出臺的背後不但有,而且還包括許多。

XPath2.0是XQuery的一個嚴格的句法子集。事實上,工作草案和語言語法都是由同一個來源自動產生的(當然是用XML和XSLT)。雖然在嚴格意義上,一個工作草案不是另一個的子集(因為某些段落是專為XPath2.0而設的),但幾乎是一樣的。不管怎樣,中有80%的文字是這兩個草案中所共有的。 XQuery1.0的XPath2.0子集要大一些。從樂觀的角度看,一旦你在學習XPath2.0的過程中透過了所有障礙,就會欣喜地發現,你幾乎已經掌握了XQuery。

事實上,XQuery所獨有的特性主要是由頂層的查詢封裝機制組成。例如的定義、名稱空間的宣告、schema的引入以及元素的構建。而在XPath2.0的另一個主要使用環境XSLT2.0中,就不需要這些機制,因為XSLT2.0一般會提供自己的版本,所以就不把它包含在共有的子集中。

對XML Schema的支援

回顧一下,XPath1.0所支援的表示式只有四種型別:

  • 節點集
  • 布林值
  • 數字(浮點數)
  • 字串
這種取值雖然簡單,不利之處是在處理其他型別資料(比如日期)時受到限制。XPath2.0引入了對XML Schema內建資料型別的支援,使能直接訪問19種簡單型別,包括日期、年、月、URI等等。此外,為了處理和構造這些不同的資料型別,還提供了幾個函式和運算子。這些內容可以在檔案中找到。

關於XPath2.0表示式的返回值的種類的詳細資料,請參閱 檔案。出於實用性,我們可以簡單地說,表示式的返回值可以是簡單型別、節點、節點或簡單型別序列。就像我們即將看到的,實際上每個表示式都返回一個序列。

XPath2.0中對節點的定義基本上與XPath1.0中相同,不同之處在於,現在可以將某些種類(元素和屬性)與XML Schema型別聯絡起來並作為這些型別進行處理。與XPath1.0一樣,有7種節點型別:檔案節點(document nodes), 元素(elements),屬性(attributes),名稱空間節點(namespace nodes),指令(processing instructions),註釋(comments)和文字節點(text nodes)。在術語上有個小差別,在這裡根節點(" nodes")現在有了個更加貼切的名稱 -- 檔案節點("document nodes")。

序列,序列,序列

作為一種處理序列的語言,讓我們來討論XPath2.0是如何對待序列的,以及它的表現。下面是一些準則,你應當牢記在心。這些關於序列的格言是XPath2.0工作的基礎。理解它們是深入理解並欣賞XPath2.0工作方式的前提。

格言之一:一切都是序列

如果你想要你的朋友對你留下深刻印象,就指著一個XPath2.0表示式(或XQuery表示式),然後不經意地說出這個表示式顯然返回一個序列。為使這個把戲逼真,你不必揭穿這個謎底:事實上所有表示式的返回值都是序列。

只要你明白這樣一個事實,即一切都是序列,你就會理解一個簡單型別值(或節點)和一個簡單型別值(或節點)序列之間沒有任何區別。基於這樣的原因,XPath2.0工作草案和一般談論時經常說返回一個“數字”或“字串”的表示式,實際上意味著“一個數值序列”或“一個字串序列”。因為這兩者之間沒有區別,兩種用法都可接受。只需記住一切都是序列是沒錯的。

格言之二:序列很淺

沒有序列之序列。如果你試圖在序列中巢狀序列,當然在句法上可以接受,不過你得到的是一個“膨脹的”序列,子序列和包含它的序列仍然是依次排列的。

例如,下面這個表示式

(2, 4, (1,2,3), 6)

經過取值變換後和這個表示式是同一個序列: 

(2, 4, 1, 2, 3, 6)

以及這個表示式,諸如此類: 

( (((2)), (4,1,2,3,(6)) )

格言之三:序列是有序的

與XPath1.0的節點集不同,序列是有序的。考慮下面這個表示式:

(/foo/bar, /foo)

就像你所知道的,逗號(,)是一個構造(連線)序列的運算子。我將 /foo 放在 /foo/bar 之後,構造的序列中 bar 元素直接在 foo 元素之前,而不管它們在原始檔中出現的先後次序。後邊,我們將會看到XPath2.0的序列可以取代XPath1.0的節點集,而不失功能性和相容性。

格言之四:序列內可以有重複

與XPath1.0的節點集(或一般的集合)不同,序列內可以有重複。例如,我們稍微修改上面的表示式:

(/foo/bar, /foo, /foo/bar)

這個序列的組成是 bar 元素, 之後是 foo 元素,接著還是同樣的 bar 元素。在XPath1.0中,我們無法構造這樣的集合,因為根據定義,節點集內不能包含同樣的元素一次以上。

節點集的興衰

在XPath1.0中,如果你想處理多個節點,就必須和節點集打交道。在XPath2.0中,節點集的概念更趨一般化,並且作了擴充套件。正如我們所看到的,序列可以包含簡單型別值以及節點。我們還看到,序列和節點集的不同點在於它是有序的,並且可以有重複。人們自然會問:如何在不破壞XPath的同時處理節點集?

在一個只有序列的世界裡如何模擬集合?

XPath1.0的節點集確實是無序的。然而,XSLT作為XPath的主要使用環境,節點集內的節點總是以某種次序進行處理的。處理節點集的預設次序就是檔案內的順序(因為所有節點的順序在檔案內總是給定的)。在XPath2.0中,用以處理節點集合(即序列)的預設順序未必和檔案中的順序相同,但是和序列內的順序是一致的。為了與XPath1.0相容,路徑表示式(以及其他1.0的表示式如聯合(union)表示式)被定義成總是返回檔案內的順序。特別地,只要在直接表示式中使用"/",結果總是以檔案內的順序。此外,總是自動從結果中除去重複的元素。XPath2.0就是這樣在一個只有序列的世界中模擬節點集的。

如果你還沒跟得上,也不必擔心。到目前為止,你可能還沒意識到XPath1.0的節點集是無序的。這對那些說明書的作者來說好處最大,它們確信一切都是一致的和完備定義(well-defined)的。姑且相信序列實際上是有序的,而路徑表示式仍然和從前一樣。

幾個要掌握的關鍵字

除了引入許多新的資料型別和函式, XPath2.0還引入了幾個基於關鍵字的運算子,我們來看看其中的幾個。

對序列的運算(Operations on sequences)

在XPath2.0處理序列的新運算子中,最強大的大概就是 for 表示式。它可以對序列進行列舉,為引用序列的每個成員返回一個新值。這一點與 xsl:for-each 很相似,不同之處在於它實際上返回的是一個序列,你然後可以按照序列對這個返回值進行操作。

我們來看下面的例子,它返回一個簡單型別的序列,每項都是各訂單中各個專案的總費用:

for $x in /order/item return $x/price * $x/quantity

然後我們就可以用 sum() 函式得到訂單的全部費用: 

sum(for $x in /order/item return $x/price * $x/quantity)

與XSLT/XPath1.0相比,像這樣的情況在XPath2.0中使用序列非常容易解決。如果不使用序列,這個問題非常難辦,通常要構造一個臨時的“結果樹片斷”,然後使用 node-set() 擴充套件函式。 

條件表示式

XPath2.0最強大(也是要求最多的)的結構之一就是條件表示式。這裡是XPath2.0工作草案中的一個例子:

if ($widget1/unit-cost < $widget2/unit-cost) then $widget1 else $widget2


定量修飾符(Quantifiers)

XPath1.0的等於運算子(=)是這個語言很有力的一個方面,這是因為它可以對節點集進行比較。我們來看這個例子:

/students/student/name = "Fred"

在XPath1.0中,如果任何一個學生的名字等於"Fred",這個表示式就返回true。這可以稱為存在定量(existential quantification) ,因為它測試滿足某種條件的成員是否存在。 XPath2.0保留了這項功能,還提供了更明確的測試方法: 

some $x in /students/student/name satiies $x = "Fred"

這個公式更加強大,因為你可以用任何想要的比較式來代替 $x = "Fred",而不僅僅是比較是否相等。而且,XPath1.0沒有提供一種手段來判斷是否每個學上的名字都是"Fred"。XPath2.0引入了這項功能來進行 全域性定量(universal quantification), 所用的句法和上面的相似。

every $x in /students/student/name satisfies $x = "Fred"


交,差,聯合 (Intersections, differences, unions)

在XPath1.0中,唯一的集合運算子是聯合(|)。這使得很難確定某個節點是否在給定的節點集內。例如,為了確定節點 $x 是否包含在 /foo/bar 節點集內,我們大約必須這麼寫:

/foo/bar[generate-id(.)=generate-id($x)]

或者 

count(/foo/bar)=count(/foo/bar | $x)

XPath2.0所引入的 intersect 運算子緩解了這種痛苦。上面的那種令人頭暈的寫法現在可以簡單地這麼寫: 

$x intersect /foo/bar

XPath2.0還引入了 except 運算子,馬上可以用來選擇某個節點集中除了某些節點之外的所有元素。在XPath1.0中如果要選擇除了某個特定名稱空間的名字之外的所有屬性,必須這樣寫:

@*[not(namespace-uri()='' and local-name()='foo')]

@*[not(generate-id(.)=generate-id(../@exc:foo)]

這時候,XPath2.0就會再救你一命,你可以用下面這種簡潔的方式寫: 

@* except @exc:foo


對資料型別的擔憂

如果你曾看過XPath2.0說明,會發現我沒有提到許多新關鍵字,包括 cast, treat, assert, 和 instance of。它們也是該語言的重要部分。但其重要性部分地取決於你在什麼環境下使用XPath2.0。如果你打算在XSLT2.0環境下使用XPath,就沒有必要每天使用它們。在某種情況下你確實想使用它們(例如,將一個字串轉換成日期),但不是非用不可。然而在XQuery1.0環境下,你會很快熟悉它們的。

原因是XQuery1.0是設計為一種靜態型別的語言(statically typed language)。在查詢之前,要考慮查詢表示式的返回資料型別,並據此對查詢進行分析和。這隻有在使用者明確指定了表示式的返回型別之後才有可能。這樣做的另一個好處是可以儘早地捕捉到錯誤,以便增強查詢的正確性。

當然,在可用性和型別性之間要有個取捨。為同時滿足兩個團體(有時人為地把使用者分為面向文件的(document-oriented)和麵向資料的(data-oriented)兩大類)的需要,XPath2.0提供了一種手段,讓使用環境可以決定在這種取捨之間站在何處。XPath2.0可以由其使用環境設定引數。這聽起來好像是解決可移植性的藥方。重要的是要清楚這些進展背後的指導原則。這個原則就是,任何XPath2.0表示式如果它不首先返回錯誤的話,則它的返回值應和其他環境下的返回值相同。這樣,如果一個表示式在一種環境下會產生錯誤,而在另一種環境下不會,則它不會產生兩個不同的表示式結果。換句話說,你得到的要麼是正確答案,要麼是錯誤資訊。正確答案永遠不可能多於一個。

對XSLT的使用者而言,大多數情況下它們不必過分擔憂。一個XPath2.0的表示式在XQuery的環境下可能會丟擲一個“異常”,而在XSLT環境下同樣的表示式只是悄悄地一個後備轉換。

結論

現在可以明確的是,XPath2.0是XPath1.0的一個非常重要的升級。它的發展動力既有XPath1.0使用者團體的要求,也有XQuery1.0對它的要求。也許你對整個方案不能認同,卻很難否認它代表了一個顯著的協同。再加上一些幸運的話,它同樣會成為幾種使用者團體的一個強大的標準工具。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992087/,如需轉載,請註明出處,否則將追究法律責任。

相關文章