XSLT 1.0 和 XPath 1.0 最初是打算為 XML 文檔提供簡單的樣式語言支持,主要是將這些文檔轉換成 Html 以呈現在浏覽器中。但是,自從 XSLT 和 XPath 可以使用之後,它們就被強行用於各種任務中,而這些任務 ― 從 XML 文檔中復雜的數據操作(聚合、單值選擇和關系旋轉)到一種 XML 形式向另一種形式的 XSLT 轉換 ― 並不是它們的設計初衷。在這些規范的版本 2.0 中,W3C 試圖使 XSLT 和 XPath 更加靈活和健壯,以便於處理這些技術的新用法。
在上一篇專欄文章中,我研究了 XSLT 的一些新特性。在本篇專欄文章中,我只研究 XPath2.0 的幾個要點 ― 由於要點太多,因而無法在一篇專欄文章中全部研究。
對於本專欄而言,特定前綴的映射含義如下:
xf: 前綴被認為映射到 XPath 2.0 函數名稱空間(http://www.w3.org/2002/08/xquery-functions)。
xsl: 前綴映射到 XSLT 2.0 名稱空間。
xs: 前綴映射到 XML 模式名稱空間。
xf:distinct-values 函數
在使用 XSLT 1.0 樣式表時,開發人員面臨的最重大的挑戰之一就是編寫與 SQL 中 SELECT DISTINCT 等同功能的 XML 代碼 ― 即一個獲取節點集並返回那些節點唯一值列表的表達式。在 XSLT 1.0 和 XPath 1.0 中這不是不可能實現的,但都是極其困難的。基本上,您都必須寫一條 xsl:for-each 語句以特殊的排序順序對每個節點求值,然後不斷回頭查看節點列表,以了解是否所有匹配特定值的任何其它節點都被處理了。有了 XSLT 2.0 ,以及引入了 xf:distinct-values 函數,這個問題就迎刃而解了。下面 清單 1中是一個 XML 文檔的快速示例:
清單 1. 帶有作者姓名的 books
<books>
<book author="Kevin Williams" title="Professional XML 2.0" />
<book author="Lewis Carroll" title="Alice in Wonderland" />
<book author="Lewis Carroll" title="Through the Looking-Glass" />
</books>
假定您想要標准化輸出作者信息,並創建一個類似 清單 2的文檔:
清單 2. 帶有書名的 authors
<authors>
<author name="Kevin Williams">
<book title="Professional XML 2.0" />
</author>
<author name="Lewis Carroll">
<book title="Alice in Wonderland" />
<book title="Through the Looking-Glass" />
</author>
</authors>
要做到這一點,使用 XPath 和 XSLT 1.0 時,需要做類似於 清單 3的工作:
清單 3. XPath 1.0 中的 SELECT DISTINCT
<xsl:template match="books">
<authors>
<xsl:for-each select="book">
<xsl:sort select="@author" />
<xsl:variable name="thisAuthor">
<xsl:value-of select="@author" />
</xsl:variable>
<xsl:if test="count(preceding-sibling::book)=0">
<author name="{$thisAuthor}">
<xsl:for-each select="../book[@author=$thisAuthor]">
<book title="{@title}" />
</xsl:for-each>
</author>
</xsl:if>
<xsl:if test="preceding-sibling::book[1]/@author != $thisAuthor">
<author name="{$thisAuthor}">
<xsl:for-each select="../book[@author=$thisAuthor]">
<book title="{@title}" />
</xsl:for-each>
</author>
</xsl:if>
</xsl:for-each>
</authors>
</xsl:template>
假定您想要標准化輸出作者信息,並創建一個類似 清單 2的文檔:
清單 2. 帶有書名的 authors
<authors>
<author name="Kevin Williams">
<book title="Professional XML 2.0" />
</author>
<author name="Lewis Carroll">
<book title="Alice in Wonderland" />
<book title="Through the Looking-Glass" />
</author>
</authors>
要做到這一點,使用 XPath 和 XSLT 1.0 時,需要做類似於 清單 3的工作:
清單 3. XPath 1.0 中的 SELECT DISTINCT
<xsl:template match="books">
<authors>
<xsl:for-each select="book">
<xsl:sort select="@author" />
<xsl:variable name="thisAuthor">
<xsl:value-of select="@author" />
</xsl:variable>
<xsl:if test="count(preceding-sibling::book)=0">
<author name="{$thisAuthor}">
<xsl:for-each select="../book[@author=$thisAuthor]">
<book title="{@title}" />
</xsl:for-each>
</author>
</xsl:if>
<xsl:if test="preceding-sibling::book[1]/@author != $thisAuthor">
<author name="{$thisAuthor}">
<xsl:for-each select="../book[@author=$thisAuthor]">
<book title="{@title}" />
</xsl:for-each>
</author>
</xsl:if>
</xsl:for-each>
</authors>
</xsl:template>
<part name="Grommets" size="3 in." color="blue" />
清單 6. 樣本零件文檔 2
<part name="Grommets" size="3 in." color="blue" />
清單 7. 樣本零件文檔 3
<part name="Grommets" size="3 in." color="blue" />
假定您現在在一個目錄中有 500 個這樣的文檔,您希望創建您所擁有的所有藍色零件的列表。無需利用中間層語言(如 Java)編寫代碼以裝入所有這些文檔並查找顏色為藍色的零件,可以編寫一個列出在哪可以找到所有零件的索引(請參閱 清單 8):
清單 8. 零件索引文檔
<parts>
<part file="part1.doc" />
<part file="part2.doc" />
<part file="part3.doc" />
</parts>
您可以編寫一個為您完成這項工作的樣式表,其代碼片段類似於 清單 9:
清單 9. 用於跨文檔的 xf:document
<xsl:template match="parts">
<blueParts>
<xsl:for-each select="xf:document(part/@file)/@color='blue'">
<part name={@name} size={@size} color={@color} />
</xsl:for-each>
</blueParts>
</xsl:template>
這種技術特別適用於分布式網絡的內容,在分布式網絡中,零件信息可以放在一台服務器上,而客戶信息可以放在另一台服務器上。通過利用樣式表(使用 xf:document 從其它 URL“拉”信息),允許該數據可以在創建它的地方起作用。
xf:current-dateTime 函數
將樣式表應用於 XML 文檔時,在輸出中包含已轉換結果的創建日期通常都很有用。當創建 Html 文檔以驅動用戶界面時,這特別重要;此類信息可以幫助高速緩存系統知道什麼時候信息的副本失效了。使用 XPath 2.0 中的 xf:current-dateTime 函數可以獲得當前日期和時間(請參閱 清單 10):
清單 10. xf:current-dateTime 示例
<xsl:value-of select="xf:current-dateTime()" />
這可能會返回 清單 11中所示的字符串:
清單 11. xf:current-dateTime 樣本結果
2002-09-17T18:22:08z
然後,可以“按現狀”使用該字符串,或者將其轉換成不同的日期格式,以便於在生成的文檔中使用。
更佳的 XML 模式兼容性
因為 XPath 2.0 目前在 XSLT 2.0 和 XQuery 1.0 之間共享,因此,XPath 需要更健壯的 XML 模式支持。實際上,XPath 2.0 中的整個數據模型目前都是強類型的:而不是簡單的字符串、數字和布爾類型,值目前使用了作為 XML 模式規范一部分定義的原語。提供了完整的函數集,因此可以把這些值顯式地從一種類型轉換為另一種類型,將這種轉換作為它們在 XPath 2.0 中操作的一部分。例如,通過使用類型名稱可以將一個值強制轉換為另一種類型,就象是一個函數一樣。因此,使用 清單 12中的代碼片段,可以將值強制轉換為無符號整數:
清單 12. XML 模式類型強制轉換示例
xs:unsignedInt(item)
XPath 2.0 中的強類型確保由 XSLT 樣式表創建的文檔能夠依據強類型的 XML 模式進行驗證。在 XSLT 2.0 之前,沒有方法可以保證這一點(例如,保證數字是無符號整數)― 為了確保樣式表不提供錯誤值,XML 模式驗證步驟是必需的。
在本文中,我只是略微談及了 XPath 2.0 必須提供的一些要點。雖然距離這項技術提升為建議書狀態還有一段時日(至少 6 個月),但是,熟悉 XPath 2.0 的特性和功能將有助於您在開始實現時能夠充分利用它。