關於本系列
W3C 發布的最新規范 XSLT 2.0 是一種轉換 XML 文檔的語言。它包括很多新的特性,其中一些特性是專門為克服 XSLT 1.0 的不足而設計的。本系列文章從 XSLT 1.0 用戶的角度(希望解決老問題、學習新技術、發現值得期待的新特性),提供了對 XSLT 2.0 的高水平概述和深入剖析。我們提供了來自常見應用程序的例子,並為那些希望升級的用戶提供了實用的建議。為了幫助您開始使用 XSLT 2.0,本文還提供了相應的遷移技術。
向後兼容使升級更容易
如果您能夠在產品環境中用 XSLT 2.0 處理程序替換所有 1.0 處理程序,但是不能馬上升級所有 1.0 樣式表代碼,本文為您提供了可用的信息。支持向後兼容(BC)特性的 XSLT 2.0 處理程序應該幾乎無需修改就能運行 1.0 樣式表,但是需要注意一些細節,而且有可能需要盡快地使用一些 2.0 特性。本系列 第 2 部分 提供了升級樣式表的五種選擇,其中三種涉及到暫時保留一些 1.0 代碼。第 3 部分 說明 XSLT 2.0 規范中定義的 BC 特性是可選的,一些廠商不一定提供。第 4 部分 更詳細地介紹了 BC 特性以及讓樣式表適應不同 XSLT 處理程序的其他工具。第 5 部分 提供了與周圍代碼版本不同的代碼島的例子。這一部分將更進一步討論 2.0 處理程序如何處理 1.0(遺留)代碼島。
XSLT 1.0 的一些特性在 2.0 中發生了變化,但是在 BC 模式下保持原來的行為。在第 4 部分介紹的所有技術中,這一部分重點介紹局部版本化,簡要提一提 @use-when、xsl:choose 和 system-property()。不涉及 xsl:fallback、xsl:if,也不討論測試可用性的函數。本文假設讀者使用 2.0 處理程序(支持 BC 特性),討論僅限於可用的內置函數。
XSLT 2.0 規范中,一些特性對於啟用其 BC 行為有專門的規定,BC 模式也影響到 XPath 表達式的求值。下表僅列舉了最明顯的影響。更多信息請參閱 XSLT 2.0、XPath 2.0 和 F&O 規范的 BC 附錄。“是” 表明 BC 模式改變了該列中所述的行為。
表 1. BC 模式的主要影響
XSLT 結構 取集合中的第一個成員 隱式轉換到需要的類型 說明 xsl:value-of 是,對於 @select 否(總是返回字符串表示)。 如果非 BC 模式或者出現 @separator 自動循環遍歷集合。 xsl:sort 是 否,如果使用舊的 data-type 屬性不需要 BC 模式。 xsl:number 是,對於 @value 是,但是僅對空列表而言。 xsl:key/@use 否 是,BC 模式下轉換為字符串。 詳見第 5 部分。 函數參數 是 是。 比較操作數(如 =、<)和算術運算符(如 +) 否 是。 2.0 中的不等關系可用於數值以外的其他數據類型。 屬性值模板(AVT) 是 間接的,因為 AVT 可以是任何 XPath 表達式,因此可以包含函數或運算符。 謂詞 否 間接的,因為謂詞可以是任何 XPath 表達式,因此可以包含函數或運算符。使用 BC 模式可能的原因
對於兩種常見的要求,BC 模式可以簡化把樣式表從 1.0 升級到 2.0 的任務:取集合的第一個成員和避免類型檢查錯誤,計算 XPath 表達式時這兩種情況都有可能出現。在 XSLT 樣式表中,XPath 表達式總是以屬性值(通常命名為 select)的形式出現,而且多數情況下會得到一個包含多個成員的集合和/或不同數據類型。這裡所涉及到的情況是 1.0 中的自動調整可能與 XPath 語言擴展及其函數集發生沖突。為了避免沖突,可以采用 BC 模式來保持 1.0 行為。如果編寫新的 2.0 代碼,則應了解版本之間的差別避免使用 BC 模式。
像 1.0 中那樣自動選擇集合的第一個成員
在 XSLT 1.0 中,a/b 這樣的表達式將得到父節點為 a 的 b 元素的節點集。如果樣式表中包含 <xsl:value-of select="a/b" />,只能得到返回的第一個 b 元素的字符串值。在 XSLT 2.0 中,a/b 將得到父節點為 a 的孩子節點 b 的一個序列。<xsl:value-of select="a/b" /> 將返回每個 b 元素的字符串值的連接,中間用空格分開。但是如果在 1.0 作用域內,2.0 處理程序將啟動 BC 模式,對同樣的指令返回序列的第一個值。對於 xsl:sort,每個排序項目都需要一個值作為排序鍵值,如果選擇了多個值,BC 模式可以避免出現類型錯誤。類似的,BC 模式還會導致選擇屬性值模板(AVT)集合中的第一個成員,AVT 是通過計算公式得到值的屬性。一般的屬性用熟悉的 name="value" 語法指定,而 AVT 用 name="{expr}" 的形式定義,花括號中是任意的 XPath 表達式。如果表達式的結果是一個序列而不是單個值,1.0 處理程序或者 BC 模式下的 2.0 處理程序將選擇第一個值並轉換成字符串(如果需要)。2.0 處理程序將接受全部的值並生成空白分隔的列表。BC 模式適用於所有 AVT,包括出現在 2.0 指令中的 AVT。對於函數參數,每個函數的簽名定義了該參數接受單個的值還是包含多個值的序列。XPath 2.0 規范 “函數調用”(3.1.5)一節詳細列出了 BC 模式影響的參數簽名,說明是否需要選擇第一個成員或者需要進行類型轉換。
像 1.0 中那樣自動調整數據類型
BC 能夠克服的最重要的不兼容問題是 2.0 不那麼情願將表達式的值轉換成需要的類型,從而造成類型錯誤,即便該表達式在 1.0 處理程序中是可接受的。數據類型不兼容可能出現在樣式表中 XPath 表達式能夠出現的任何地方,包括函數參數、AVT 或者范式的謂詞中。函數規定了參數需要的類型,數學操作數和比較運算符也需要兼容的類型。如果不使用需要模式驗證的輸入源,通常不用擔心數據類型。非驗證輸入文檔屬性和文本節點的值用專門的類型注釋(原子值用 xs:untypedAtomic)表示,因此可以動態地轉換成需要的類型。如果處理驗證的輸入源,或者強制轉換或構造標記為模式類型的原子值,如 xs:date 或 xs:decimal,操作數或者函數參數就不能動態地轉換,處理程序將拋出類型錯誤。必須修改表達式來適應不同的類型。但是如果啟用 BC 模式,轉換就能自動進行。如果 2.0 處理程序沒有 BC 模式,可以為類型可能不正確的任何值加上強制轉換函數。不需要明確使用數值類型時,number 函數仍然可以使用。如果需要在創建的時候將變量轉換成需要的類型,可使用 as 屬性。(清單 2 有一個例子。)
無論如何 2.0 仍然存在的一些差別
雖然本文主要討論使用 BC 模式可以控制的一些問題,但 XSLT 2.0 仍然有些東西徹底改變了。那些雖然不同但是只要不在 1.0 中使用就不會造成錯誤的最主要的變化包括:
內置模板傳播收到的任何參數。只有依靠內置模板並且 靠它們對其孩子節點顯式調用模板時才會出現這種情況。
和其他數據項一樣,1.0 中返回數字的項 system-property('xsl:version') 在 2.0 中返回一個字符串,關於處理這種變化的辦法請參閱本系列 第 5 部分 的第四個例子。
沒有分配優先級且匹配范式 “/” 的模板默認具有較低的優先級。只有當多個模板和根匹配的時候才會遇到這種情況。
更多的字符串能夠轉換成數字,最明顯的是科學記數法表示的浮點數(如 1.234E17)。
在 XPath 1.0 下轉換成 NaN 的字符串在 XPath 2.0 中可能轉換成其他值(使用 number 函數或者在函數調用中隱式轉換)。
需要改變代碼的錯誤
對於某些不明確的 1.0 處理程序能夠容忍的情況,XSLT 2.0 版更嚴格,將報告錯誤。BC 模式能夠克服一些 2.0 的嚴格性,但是不能全部避免。所幸的是,只有少數在 1.0 中工作但在 2.0 中變成錯誤的項目對 BC 模式免疫。工作方式不同並造成錯誤(在 1.0 中不會)的比較重要的變化有:
對於某些輸入,比如 NMTOKENS 類型的輸入,sum 函數可能拋出錯誤而不是返回 NaN。
不再允許使用順序比較運算符(如 A < B < C)。
XPath 表達式中一些相鄰的標記需要用空格分開。比如 10div 3 現在需要在 10 和 div 之間增加空格。
有更多的輸出構造問題需要引發錯誤而不是避開。比如,創建名稱無效的屬性必須引發錯誤。
允許廠商不 支持 disable-output-escaping 屬性。如果 2.0 處理程序不支持,該屬性將導致錯誤。
島是如何工作的
在本文中,島(2.0 中的 1.0,或者 1.0 中的 2.0)指的是樣式表中被 XSLT 處理程序區別對待的元素的子樹。具體來說,2.0 樣式表中的 1.0 代碼島將激活 BC 模式。XSLT 1.0 中,只能在 xsl:stylesheet 或 xsl:transform 元素上設置 version 屬性。因此只有被包含或者導入的樣式表才存在島,這些樣式表在 2.0 規范中稱為樣式表模塊。在 XSLT 2.0 中,樣式表上的任何元素都可有 version 屬性(對於 xsl:output,version 屬性有不同的含義)。島最常見的作用域是樣式表模塊、模板(比如下面的 清單 1 中)或者單個 XSLT 元素(如下面的 清單 2)。2.0 中,version 屬性支持向後兼容或向前兼容(FC)。關於在什麼地方設置 version 屬性以及它影響哪些元素的詳細討論,請參閱 第 4 部分 “標准屬性和作用域”。
通過局部版本化啟用 BC
所謂 2.0 中的 1.0 島,就意味著 XSLT 2.0 處理程序將以 BC 模式執行島中的指令。這些島用值為 1.0 的 version(或 xsl:version)來標識。不要認為 BC 模式下指令或表達式的行為與其在 XSLT 1.0 處理程序中相同。當一個指令或表達式啟用 BC 模式的時候,XSLT 2.0 處理程序並不要求只能使用 1.0 語法。因此可以在 1.0 島中使用所有 2.0 新特性,比如 xsl:for-each-group 和 xsl:function,並且只有存在 XPath 表達式的地方新特性才會受到 BC 模式的影響。(樣式表函數和分組在本系列第 1 部分中描述。)
1.0 代碼島的例子
清單 1 顯示了一個簡單的使用 BC 模式的例子,以 xsl:value-of 和節點與數字的比較為例:
清單 1. 作為 1.0 代碼島的模板
<xsl:stylesheet version="2.0" ...>
<xsl:template match="somenode" version="1.0">
<xsl:param name="x" />
<xsl:if test="$x > 1 ">
<xsl:value-of select="$x" /> is greater than 1
</xsl:if>
</xsl:template>
<!-- etc. -->
</xsl:stylesheet>
分析:如果傳遞給該模板的參數值可能是一個節點序列,比方說 2 3 4,則非 BC 模式下(即沒有 version 屬性)該模板的結果就是 "2 3 4 is greater than 1"。如果這是從 XSLT 1.0 樣式表中復制過來的,可能希望結果是 "2 is greater than 1",因為編寫該模板時認為只返回第一個節點。加上 version 屬性後,BC 模式就起作用了,模板的結果為 "2 is greater than 1",因為除了第一項外序列中的其它項都被 xsl:value-of 丟棄了。如果傳遞給該模板的參數值可能是一個 xs:string(比如文字 “2”),非 BC 模式下,xsl:if 中的 test 表達式將產生錯誤,因為 XPath 2.0 不允許比較字符串和數字。但是如果啟用 BC 模式,處理程序就會把字符串轉換成數字並完成比較。結果仍然是預期的 "2 is greater than 1"。
建立和引用變量
如果向後兼容起作用,將影響到島中的所有 XPath 表達式,雖然結果可能相同也可能不同。無論是變量引用、節點還是兩個項的乘積,BC 模式都會影響表達式的執行。如果變量在島的范圍之外建立,但變量引用在島內,BC 模式仍然會影響和變量有關的值。相反,xsl:variable 元素中的指令不受 BC 模式影響,像通常一樣按 2.0 執行,即使變量引用在島內。
清單 2. 作為 1.0 代碼島的 XSLT 指令
<xsl:stylesheet version="2.0" ...>
<xsl:variable name="x" as="text()*">
<xsl:value-of select="('abc','def')" />
<xsl:text>xyz</xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="$x" version="1.0"/>
</xsl:template>
</xsl:stylesheet>
分析:模板中帶 select="$x" 的 xsl:value-of 指令啟用了 BC 模式。它僅僅從變量 x 取序列的第一個值。但是設置變量 x 的指令並不在 BC 模式下執行。因此,該變量包含兩個文本節點,abc def 和 xyz(如果變量聲明也啟用 BC 模式的話,則為 abc 和 xyz)。因此 <xsl:value-of select="$x" version="1.0" /> 返回 abc def。
1.0 中的 2.0 島
如果使用 XSLT 1.0 處理程序,1.0 樣式表中也可存在 2.0 島。如果主樣式表模塊導入或包含的樣式表模塊帶有 xsl:stylesheet 元素中定義的 version="2.0" 屬性,則導入/包含樣式表中的所有指令都在 FC 模式下執行。關於 FC 模式的更多信息請參閱本系列 第 4 部分。
如果使用 XSLT 2.0 處理程序,當您為了禁用 BC 而在 1.0 島中嵌入 2.0 島的時候,就可能存在 1.0 中的 2.0 島。這樣做可能是由於為了增強可重用性而從 1.0 樣式表中復制了一些代碼,但是發現在復制的代碼中采用一些 2.0 特性也不錯。嵌套 version 屬性以達到這樣的效果,必須小心地跟蹤代碼,保證在需要的地方啟用和禁用 BC 模式。否則,意料之外的結果可能很難調試解決。因此,一般應避免嵌套 version 屬性,花些時間將復制的 1.0 代碼改寫成不需要 BC 並且結果和 1.0 類似的 2.0 代碼。比方說,不需要 BC 模式來強迫選擇節點集中的第一個成員,只要在原來的表達式中加上謂詞 [1] 即可。
版本高於 2.0 的島對 1.0 和 2.0 處理程序都會觸發 FC 模式。本文結尾的 後驗法 一節對於如何應對這種變化提供了一些建議。
向後兼容模式詳解
本文的 例子 中您看到了一個模板級的島,第 5 部分 有一個不同導入樣式表模塊采用不同版本的例子。(即 “導入優先級” 那個例子。)第 5 部分的另一個例子(“可用元素”)說明了處理程序如何根據版本和處理程序功能執行、跳過或者篩選掉(用 @use-when) xsl:choose 塊中的某些 xsl:when 子元素。
為了保證它的一個屬性中的 XPath 表達式能夠以 1.0 兼容的方式解釋,也可以在單個 XSLT 元素上使用局部 1.0 版本化。類似的,也可在字面結果元素(LRE)上使用 xsl:version="1.0",保證屬性值模板(AVT)中的表達式按照 1.0 處理。第 4 部分解釋了 BC 模式如何允許未知的擴展函數成為動態而非靜態錯誤。(非 BC 模式下,2.0 處理程序將在檢查樣式表靜態錯誤的初期階段引發未知擴展函數錯誤。作為標准 XSLT 函數集的一部分被明顯引用的函數也在靜態檢查階段出現錯誤。)請注意,version="1.0" 並沒有關閉新特性,因此甚至能夠在新增的 XSLT 元素如 xsl:namespace 上設置,如果在這裡設置 BC 模式作用域最合適的話。
如果使用多個 2.0 處理程序,而且這些處理程序並沒有都實現 BC 特性,您可能陷入進退兩難的境地。通過在 use-when 篩選器中檢查 system-property('xsl:supports-backwards-compatibility'),可以排除掉 1.0 模塊或者模板。要記住,不支持 BC 模式的 2.0 處理程序對於需要執行或者在聲明中設置的任何 version="1.0" 都會引發錯誤,除非用 use-when 屬性篩選掉。
手工控制包含的例子
一些站點有多台運行 XSLT 的服務器,對轉換工作進行劃分,每台服務器專門處理某個方面,比如語言和地區。您可能希望所有的服務器都使用相同的樣式表,用 @use-when 篩選來改變特定的模板或者聲明。但是可用於這類篩選的表達式是有限的,本系列 第 4 部分 討論這一點。具體來說,作為 @use-when 值的表達式不能訪問任何參數或變量,也不知道 第 3 部分 討論的任何啟動選項。篩選在樣式表的編譯時 執行,但是這裡描述的特殊類型應該在更早的構建時執行。
清單 3 給出了一種方法,可以用一組樣式表模塊組合成不同的樣式表。每次構建需要做的修改僅限於主樣式表中的一小部分。這種技術類似於注釋掉某些 xsl:import 或 xsl:include 聲明,但是修改注釋標記更容易出錯。利用這種技術,可以在代碼中包含真正的注釋,在包括或者排除相鄰的代碼時不用擔心注釋的定界符。
清單 3. 構建時的變化設置
<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- ======== Edit this part at build time to set the flavor. ======== -->
<!-- Set the use-when to true() for the English (US/Canada) flavor. -->
<xsl:include href="appearance-ENus.xsl" use-when="true()"/>
<!-- Set the use-when to true() for the English (UK) flavor. -->
<xsl:include href="appearance-ENuk.xsl" use-when="false()"/>
<!-- Set the use-when to true() for the French (Canada) flavor. -->
<xsl:include href="appearance-FRca.xsl" use-when="false()"/>
<!-- Set the use-when to true() for the French (France) flavor. -->
<xsl:include href="appearance-FRfr.xsl" use-when="false()"/>
<!-- ======== Exactly one of the above should have true() in the use-when. ======== -->
<!-- ======== Make no changes below here except under revision control! ======== -->
<!-- Included modules that are common to all flavors. -->
<xsl:include href="utilitIEs.xsl"/>
<xsl:include href="gentemplates.xsl"/>
<!-- Declarations that are common to all flavors. -->
<xsl:output method="Html"/>
<xsl:template match="/">
<!-- Initial template can be here, while others might be in other modules. -->
...
</xsl:template>
</xsl:stylesheet>
前述技術最好用於擁有清晰劃分的所有地區變體(或者需要的構建變體),並且捆綁到上層處理的一兩個模塊的情形。如果所有變體的所有模板都在主樣式表而不是包含模塊中,改變布爾值更加麻煩。上面的例子中只需要改變一個布爾值就能排除一個模塊。集束可以隱藏更深包含層次上共享的一些代碼,該例中 appearance-FRca.xsl 和 appearance-FRfr.xsl 都能包含一個模塊,其中包括適用於所有國家法語的外觀方面。該技術也可用於其他構建時變體,比如某個模板的普通版本和跟蹤版本。
島和聲明
對於作用域,XSLT 指令元素可以通過樣式表模塊或者包含它們的模板分離開。聲明元素只能通過模塊分離。(關於作用域的解釋以及聲明和指令之間的區別,請參閱本系列 第 4 部分,關於新 2.0 聲明的可移植性的討論請參閱 第 5 部分。)多數聲明都是從 1.0 帶來的,很少變化或者根本沒有變化,除了 xsl:key 以外,通常不需要考察舊聲明的內容。但可能需要改變的是,1.0 中將所有聲明都放在主樣式表中表示全局應用范圍的傳統做法。
隨著升級樣式表代碼和用模塊隔離島,將聲明放在與使用該聲明的模板聯系更緊密的島模塊中可能更方面、更容易閱讀。可能希望增加模塊的數量,以便生成島和實現類似 例 3 所示的技術。樣式表模塊的結構通過 xsl:import 和 xsl:include 聲明實現,兩者的用法和原來一樣。
2.0 中發生了有意思的變化的一個聲明是 xsl:key,為了支持非字符串數據類型、匹配字符串的排序以及更復雜的鍵值訪問,它被改進了。此外,BC 模式對 xsl:key 的影響有專門定義,支持原來形式的值匹配。作為分類聲明和相關函數的例子,可以將某個命名鍵 xsl:key 聲明和對同一命名鍵使用 key 函數的所有模板放在一個樣式表模塊中。首先,可以在整個模塊上設置 version="1.0",這樣鍵的值匹配就符合 1.0 規范中基於字符串的匹配方式。以後將該模塊改為 2.0 版本重新審查這個鍵的時候,可能希望變為不同的數據類型(比方說 xs:time),相應的還要修改 key 函數參數和 xsl:key 聲明。如果得到的結果不對,只要與樣式表的其他部分清晰地劃分開(按照功能),就可以恢復模塊的舊版本。
2.0 還在序列化方面做了一些改進,其中部分已在本系列的 第 1 部分 介紹。觀察樣式表如何生成最終結果的時候,您可能希望檢查一下 xsl:output 聲明。因為通常在一處指定一組輸出屬性,因此島和模塊對序列化影響很小。如果樣式表生成多種類型的輸出(比如 XML 和文本),可能對使用命名輸出規范感興趣。
影響遷移難度的因素
表 2 列出了遺留 1.0 樣式表由 2.0 處理程序執行時可能帶來意料之外的差別的一些方面。(省略了一些情況,或者是即便在 1.0 中也不合法的樣式表代碼,或者是只有當使用 2.0 特性才會出現問題的情況。)可用該表預測遺留樣式表原樣在 2.0 處理程序中會得到什麼結果,如果僅僅在開始改為 version="2.0" 其他不變會怎麼樣。如果 BC 模式掩蓋了問題,則給出另一種補救方式來避免對 BC 模式的依賴。
表 2. 解決兼容性問題
警戒信號 補救措施 更嚴格的靜態錯誤檢查,比如 xsl:number 對 @value 和 @count 都引發錯誤 根據需要修改這些指令 對 xsl:call-template 傳遞的意料之外的參數進行更嚴格的檢查 簡單的辦法是在接收模板上加上 version="1.0",這樣對於多余的參數就不會拋出錯誤了。要真正修改,看看該模板名的每個 xsl:call-template,試著確定所有可能的參數或者為何調用傳遞它們。 名稱空間軸的使用 2.0 中對該軸的支持是可選的,不過 BC 模式要求支持它。提取前綴和 URI 的新函數的使用。 樣式表需要保證名稱空間聲明使用特定的前綴。 樣式表作者可以進一步控制前綴。使用 xsl:namespace 之類的工具練習這種控制。 至少有部分輸入是 XHTM。 在 xsl:output 中明確使用 method="xHtml" 而不要信任默認動作。 屬性值、注釋或者處理指令的內容比 1.0 中更多 一些 1.0 處理程序悄悄地忽略在值中創建非文本內容的嘗試,而 2.0 規定必須得到該字符串。看看構造函數,確保只創建了需要的字符串。 format-number() 中的格式(picture)字符串具有前後是“被動”字符的數字標識符,通常是結果中的文字。 僅對數字本身使用 format-number(),然後使用 concat() 給返回的值追加或預追加被動字符。如果 picture 字符串對負數有單獨的 sub-picture,這個特殊問題請參閱 XSLT 2.0 規范 的 16.4 節。 輸入文檔包含名稱與新增 XPath 保留關鍵字: every、for、if、some(這四個可能作為語法標記序列中的第一個,很可能造成沖突)相同的元素。 使用明確而非隱式的的孩子軸指定器。比如將表達式中的 every 改為 child::every。 對也是合法數字且沒有為 xsl:sort 指定數據類型的字符串排序。 如果作為字符串排序,單獨保留 xsl:sort 元素即可。如果要作為數字排序,使用 number() 或者類型轉換函數。如果作為時間、時期或者其他有序類型排序,使用類型轉換函數。 帶有 select 表達式(返回多個值)的 xsl:sort 報告類型錯誤(XSLT 1.0 只能按照文檔順序選擇第一個值。) 使用 BC 模式,或者在 select 表達式後加上謂詞 [1]。 排序字符串,一些項存在但長度為零,一些不見了 如果擔心這些例外情況的順序,需要修改表達式。比如,如果 1.0 樣式表中有 <xsl:sort select="@x">,應改為 <xsl:sort select="string(@x)"> 或 <xsl:sort select="if (@x) then @x else ''"> 來達到同樣的效果。 依賴於特定的數字轉換,得到 NaN 或者擴展 IfExpr 引入非數字的特殊情況,或者像 1.0 那樣使用 number() 強制顯式轉換得到 NaN。 操作正和負無窮大,尤其是如果除以零得到這樣的值 使用 xs:double 構造器函數和 'INF'、'-INF' 生成無窮大。像這樣檢查:if ($x eq xs:double('INF')) ...(如果是其他數據類型,$x 應該擴展轉換為 double)。 依賴 xsl:key 匹配字符串值 如果要像 1.0 中那樣作為字符串匹配,使用 xs:string() 類型轉換函數。另一種不那麼具有可移植性的辦法是在 xsl:key 聲明中加上 version="1.0",前提是原來不在 1.0 作用域內。 使用擴展函數,用 xsl:choose 測試保證函數的可用性 BC 模式允許這種預防措施,但 2.0 方法使 @use-when 測試可用性。這種技術請參閱本系列 第 5 部分 “智取不支持的擴展函數”。順便說一下,如果新增的眾多 2.0 函數有一個能夠完成這項工作,可能就不需要使用擴展函數了。可以用轉換樣式表的樣式表做一些簡單的修正。
對新特性使用島(在 1.0 代碼的海洋中)
本文主要討論如果總體設為 2.0 版、僅在必要的地方使用更低版本,這種情況下如何解決遺留代碼的問題。 第 2 部分 所述五個選項中的 2 和 4 對大部分代碼使用較新的版本。對於穩健升級的謹慎方法,即其中的第 3 種選項,可以嘗試把新特性隔離出來,樣式表的大部分保持 1.0 版。這樣大部分代碼就要在 BC 模式下運行。一些特性比另一些更好隔離。下面是可以繼續采用選項 3 和使用 2.0 島的一些標志。如果您最關注的 2.0 特性出現在列表中,隔離每個新的用法就大有希望:
xsl:value-of 改進
通過隱含文檔節點導航(請參閱 第 1 部分)
僅在少數模板中用到的序列或者新的數據類型
Tunnel 參數,如果很容易隔離相關模板棧
樣式表函數,如果僅在少數模板中使用(請參閱 第 1 部分)
數字以外的其他數據類型上的不等關系(比如小於)
xsl:key 增強(請參閱 第 5 部分)
在內置模板中使用參數(編寫顯式的模板更安全)
ifExpr、rangeExpr 以及 quantExpr 的 XPath 運算符
用 tokenize() 或正則表達式分解字符串
XHtml 輸出方法
xsl:result-document
增強
xsl:number
format-number()(不是功能增強,而是文檔更清楚了)
對輸出的復雜逐字符控制(請參閱 第 1 部分的 “輸出增強” 一節)
為未來作准備
2.0 規范為保持遺留樣式表的可用性做了很多准備,至少對支持 BC 的處理程序而言。記住這一點,再加上 1.0 版本下也能使用新 2.0 特性的能力,您也許認為遺留代碼能無限期的保存下去了吧。但是要考慮到為理解跨版本兼容的細節而付出的額外的學習時間。應付這些多余的規則增加了代碼可維護性的困難,因為這些規則只能用於非常具體的情況。如果 BC 模式的作用域過寬,修改代碼的時候就很可能犯錯誤,尤其是如果修改者沒有檢查處在哪個版本控制下的情況下。從表 2 可以看出,如果每次嘗試修改相關的少數問題,很容易完成一些固定的修改來把代碼升級到 2.0。
隔離相關的功能對於准備改寫代碼是一種好辦法,不論是否因為處理程序升級。隔離需要使用包含或導入的樣式表模塊。不需要預測 2.0 之後 XSLT 還會做哪些改進,只需要將相關的模板和聲明放到單獨的模塊中就行了。即使不考慮其他,至少為每個模塊設置版本號很容易,本文和 第 5 部分 介紹了如何混合不同的版本。事實上,XSL 工作組正努力使混合版本更容易,根據所有樣式表模塊必須有版本號這一先見之明,甚至在新的處理程序出現之前就能編寫後 2.0 代碼。
XSLT 1.0 和 2.0 可移植工具箱的變化比任何新增 2.0 特性都更復雜。(詳情請參閱本系列的 第 4 部分。)遺留樣式表中可能為保證跨 1.0 處理程序的可移植性已經存在一些預防代碼(比如 xsl:choose 測試特定擴展函數的可用性)。現在 XSLT 還必須解決跨版本的可移植性,較老的防護代碼通常依賴於兩個條件:BC 模式和這些防護代碼檢查的條件。1.0 的一些可移植技術只能用於 BC 模式,另一些只能用於 FC 模式。樣式表可以引導 XSLT 處理程序進入 BC 或 FC 模式,或兩者都不能,但不能同時。激活或取消這些模式的 version 屬性可能隱藏在樣式表中,不一定那麼直觀。因此,在 BC/FC 模式無效的時候能工作的防護技術最容易閱讀,也最具有後驗性。對於 2.0 處理程序,這主要是指 @use-when 篩選,它在靜態錯誤掃描之前進行。
如果樣式表的版本號比處理程序低
雖然本文詳細介紹了 2.0 處理程序可能作為錯誤拒絕的一些 1.0 樣式表結構,但這些並非 XSLT 技術的主流。1.0 中的所有 XSLT 元素及其所有遺留屬性(@disable-output-escaping 除外)在 2.0 中仍然存在,也包括舊的函數。不需要防護技術來防止這些老的結構造成錯誤,因為不能被 2.0 處理程序識別,只需要保證達成預期目標即可。(一些 2.0 處理程序可能不識別名稱空間軸,請參閱本系列的 第 3 部分。)沒有理由認為未來的 XSLT 版本(假設有的話)會比對待 1.0 結構更嚴厲。但是,BC 模式在 2.0 處理程序中作為一種可選特性存在這一點也表明,維持老的代碼並非最佳辦法。XSLT 3.0 處理程序(假設)也會出現類似 1.0 和 2.0 代碼的問題,將來也是如此。