DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 准備從 XSLT 1.0 升級到 2.0,第 5 部分: 使樣式表可用於任何處理程序版本
准備從 XSLT 1.0 升級到 2.0,第 5 部分: 使樣式表可用於任何處理程序版本
編輯:XML詳解     

 關於本系列

  W3C 發布的最新規范 XSLT 2.0 是一種轉換 XML 文檔的語言。它包括很多新的特性,部分是為克服 XSLT 1.0 的不足而設計的。這組文章從 XSLT 1.0 用戶的角度(希望解決老問題、學習新技術和發現值得期待的新特性),提供了對 XSLT 2.0 的高層次的概述和深入剖析。我們提供了來自常見應用程序的例子,並為那些希望升級的用戶提供了實用的建議。為幫助您開始使用 XSLT 2.0,本文介紹了一些遷移技術。

  致不能馬上放棄 XSLT 1.0 處理程序的讀者

  本文詳細介紹了在 XSLT 樣式表中混合使用 1.0 和 2.0 版特性。說明了為何 2.0 版元素對於 1.0 版處理程序不一定是致命的,以及如何在必要的時候分離新舊代碼。

  大家熟悉的 XSLT 1.0 在設計的時候已經考慮到將來可能制定新的版本。最明顯的證據就是所有樣式表外層的 xsl:stylesheet 元素都需要有 version 屬性。所有 XSLT 處理程序,包括 1.0 處理程序,都要求支持向前兼容(FC),就是說即使樣式表的版本號高於處理程序的版本號,該處理程序仍然能夠解釋樣式表。如果要讓 FC 對樣式表生效,處理程序對於 XSLT 聲明和指令中的未知屬性更加寬容。處理程序還必須忽略聲明層上的新 XSLT 元素而不報錯。要注意,2.0 規范關於 FC 的概念有微妙的變化,因此要閱讀和處理程序版本匹配的規范才能獲得正確的預期結果。

  XSLT 2.0 處理程序必須支持 FC。但是不一定支持向後兼容(BC),這種情況下樣式表中只要出現 version="1.0" 都會報錯,除非是 xsl:output 中(設置 XML 版本)。本系列的 第 2 部分 介紹了關於一次性切換到 2.0 的一些決定性因素,如果必須在使用的產品中保留 1.0 處理程序,本文將從 1.0 的角度介紹一些有用的技術。

  版本間的可移植性

  這一節中的每個例子都有兩個或三個獨立的代碼分支,但是對於任何給定的 XSLT 處理程序只有其中的一個會執行。示范了可移植性工具箱中的每個工具的用法。

  必須以可移植的方式選擇執行路徑

  對於跨版本移植工具箱中的所有技術(請參閱本系列文章的 第 4 部分),1.0 和 2.0 處理程序對其處理方式都略有不同。因為這些技術或者預防措施 就像是分界點,如果希望樣式表能夠用於每種版本的處理程序,必須考慮每個處理程序版本如何處理它們。@use-when 屬性是作為良好預防的一個很好的例子,但是不能用於 1.0 處理程序。本文將給出一些例子,說明 FC 方式下的 1.0 處理程序可以允許它的存在,這樣就可以在使用 2.0(或更高版本)的處理程序時利用這種預防措施,而 1.0 處理程序仍然可以像 @use-when 不存在情況下那樣處理。

  另一個例子是 system-property() 函數,兩個版本中都有該函數,但是 2.0 中它的參數可使用更多的關鍵字。如果 1.0 處理程序運行包含該函數的代碼,就只能使用 1.0 所知道的關鍵字。具體來說,1.0 只能識別 xsl:version 這一個關鍵字,它保存處理程序的版本。

  如果希望 1.0 處理程序執行某些指令而 2.0 處理程序忽略,可以結合使用上述兩種預防措施: use-when="number(system-property('xsl:version')) < 2.0" 實際上可用於任何版本!還有一種辦法,雖然可讀性不夠好,但是也能工作,即 use-when="not(element-available('xsl:next-match'))"(就是說僅用於 2.0 處理程序的所有指令都放在另一個分支代碼塊中)。不過,這些辦法只能允許 2.0 代碼塊將這些測試其中之一用於只為 1.0 孤島設計的元素。沒有辦法保留測試(>= 或者去掉 not)來防止 1.0 處理程序執行僅用於 2.0 的孤島。

  XSLT 還提供了使用 xsl:choose 或 xsl:if 選擇代碼執行路徑的傳統方法,可以與 use-when、system-property()、function-available() 以及 element-available() 相結合解決任何版本化問題。和 @use-when 類似,xsl:choose 或 xsl:if 中的 function-available() 也有一個缺點,第 4 部分 對此做了介紹。總之,工具箱足以滿足您的需要,雖然要正確使用它還需要一點創造性以及對 XSLT 1.0 與 2.0 的正確理解。

  根據版本選擇代碼示例

  可以使用 use-when 屬性完成向前和向後兼容處理。缺點是 1.0 處理程序不能識別該屬性,無論是否啟用 FC 方式,只要樣式表包含 version = "1.0" 屬性(即不采用 FC 方式)都會報錯。下面的例子說明如果使用支持未來 XSLT 版本(比如 3.0)的處理程序,並且希望樣式表根據處理程序版本的不同采取不同的方式處理,如何使用 use-when 屬性達到目標的一個簡單示例。

清單 1. 使用 xsl:choose 和 @use-when 根據版本 1.0、2.0 和 3.0 選擇代碼的一般示例

<xsl:stylesheet version="3.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 
<xsl:template match="/"> 
 <xsl:choose> 
  <xsl:when test="number(system-property('xsl:version')) = 1.0"> 
   <!-- 1.0 instructions --> 
  </xsl:when> 
  <xsl:otherwise> 
   <xsl:sequence use-when="number(system-property('xsl:version')) = 2.0"> 
    <!-- 2.0 instructions --> 
   </xsl:sequence> 
   <xsl:sequence use-when="number(system-property('xsl:version')) = 3.0"> 
    <!-- 3.0 instructions --> 
   </xsl:sequence> 
  </xsl:otherwise> 
 </xsl:choose> 
</xsl:template> 
 
</xsl:stylesheet> 

  分析:1.0 處理程序將啟用 FC 方式(因為樣式表版本是 3.0),但是 xsl:when 測試成功因而執行該子句。2.0 處理程序也啟用 FC 方式,但 xsl:when 測試失敗,轉而執行 xsl:otherwise 分支,use-when 屬性使它只能看到第一個 xsl:sequence 塊的內容。由於 FC 方式,1.0 和 2.0 處理程序都不會對更高版本的指令拋出靜態錯誤。假想的 3.0 處理程序將進入 xsl:otherwise 分支,而 use-when 屬性將使它只能看到第二個 xsl:sequence 塊的內容。

  模板層的分支

  1.0 處理程序如何使用 use-when 屬性實現向前兼容呢?下面的辦法使用 use-when 屬性篩選模板,無論 1.0 還是 2.0 處理程序,甚至存在 1.0 處理程序不能理解的 2.0 結構都不會報錯。

清單 2. 模板層分支:模板/優先級篩選

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 
<xsl:template use-when="number(system-property('xsl:version')) = 1.0" 
       priority="10" match="/"> 
 <xsl:text>Running in 1.0</xsl:text> 
 <!-- some 1.0 instructions --> 
</xsl:template> 
 
<xsl:template use-when="number(system-property('xsl:version')) >= 2.0" 
       priority="9" match="/"> 
 <xsl:text>Running in </xsl:text><xsl:value-of select="system-property('xsl:version')" /> 
 <xsl:for-each-group select="nodes"> 
  <!-- some 2.0 instructions --> 
 </xsl:for-each-group> 
</xsl:template> 
 
</xsl:stylesheet> 

  分析:這個例子之所以成功,是因為啟用 FC 方式的 1.0 處理程序將忽略 use-when 屬性並運行第一個模板 —— 它的模板優先級高於第二個模板(相同的匹配范式)。2.0 處理程序則根據每個模板 use-when 屬性中的 XPath 表達式,確定轉換中只能使用第二個模板。執行 xsl:apply-templates 指令的時候第一個模板已經消失。

  推薦應用:xsl:import 與 @use-when 和導入優先級結合使用

  這個示例將把在 1.0 和 2.0 代碼中獲得等價效果的命名模板分離開。下面推薦一種使用導入優先級的方法,可以讓多種類型的命名 XSLT 元素覆蓋其他同名的元素。導入優先級機制在 1.0 和 2.0 處理程序中的工作原理是一樣的,盡管在 2.0 中可用於更多的命名項。如果兩個模板名稱相同而導入優先級不同,不會出現錯誤,而處理程序將選擇具有較高優先級的模板。主樣式表具有屬性 version="1.0" 並避免出現 1.0 不知道的 XSLT 元素。主樣式表導入imp1。imp1中的一些元素可能使用 use-when 屬性進行篩選,因此imp1需要使用屬性 version="2.0" 來觸發 FC 方式避免報錯。imp1中的所有模板內容對於 1.0 處理程序是安全的。第一次導入(imp1)引入了第二個樣式表模塊(imp2),它具有屬性 version="2.0",其中包含需要使用的一些僅能用於 2.0 處理程序的代碼。下面是摘自主樣式表模塊的部分片段:

清單 3. 主樣式表模塊片段

<xsl:stylesheet version="1.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:import href="imp1"/> 
 
<xsl:template match="/"> 
  <!-- calls the fit-string template somewhere, but all 1.0 code in this template --> 
</xsl:template> 
 
<!-- other 1.0-only templates --> 
</xsl:stylesheet> 

  它導入的樣式表模塊imp1具有下面的基本結構:

清單 4. 導入樣式表模塊的基本結構

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:import href="imp2"/> 
 
<xsl:template name="fit-string" use-when="number(system-property('xsl:version')) = 1.0"> 
  <!-- Performs this Operation the old and hard way. 
   All the code in this template is 1.0 (the legacy code). --> 
</xsl:template> 
 
</xsl:stylesheet> 

  這樣imp1導入imp2,後者有一個 xsl:function 聲明,這是 2.0 新增的,命名模板的一個替代版本使用該函數。函數的基本結構如下:

清單 5. xsl:function 的基本結構

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform" 
XMLns:my="http://www.example.com/NS/functions" exclude-result-prefixes="my"> 
 
<xsl:function name="my:pad"> 
  <!-- a pIEce of the task that is best packaged as a function --> 
</xsl:function> 
 
<xsl:template name="fit-string" 
       use-when="number(system-property('xsl:version')) >= 2.0"> 
  <!-- Performs this Operation the new and easy way, using my:pad() somewhere. 
   There may be other 2.0 code. --> 
</xsl:template> 
 
</xsl:stylesheet> 

  分析:1.0 處理程序將執行所有的導入操作,imp1中 fit-string 模板的版本將得到更高的優先級。2.0 處理程序也執行兩次導入操作,但是僅對imp1中的 use-when 屬性起作用並刪除其中的 fit-string 模板。2.0 處理程序初始化 my:pad 函數並調用 fit-string 的imp2版本,後者使用該函數。

  測試函數或元素的可用性

  預防不可用的函數或元素的一種方法是在同一模板中提供替代代碼路徑。一種結構可能會被認為不可用,因為是擴展或者來自 XSLT 的未來版本。但是,在 1.0 和 2.0 中 “可用” 或 “擴展” 的概念略有不同,因此盡管查看要使用的處理程序所屬版本的規范雖然是老生常談,仍然有必要再次強調。如果需要的函數不可用,就必須繞開調用該函數的整個 XPath 表達式。下面的三個例子中,解決不可用函數的最終辦法是調用命名模板。每個例子中,函數或者命名模板的結果都保存到變量中以便在其他 XPath 表達式中引用。

  選擇函數和非函數方法

  下面是一個簡單的例子,說明了如何在支持的時候使用內置函數來優化性能,不得已的情況下使用命名模板。

清單 6. 選擇內置 2.0 函數、擴展函數和非函數方法

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform" 
  XMLns:set="http://exslt.org/sets" extension-element-prefixes="set"> 
   
 <xsl:template name="makeList"> 
  <xsl:param name="set" /> 
  <xsl:variable name="distinctSet"> 
   <xsl:choose> 
    <xsl:when test="number(system-property('xsl:version')) >= 2.0"> 
     <xsl:sequence select="distinct-values($set)" /> 
    </xsl:when> 
    <xsl:when test="function-available('set:distinct')" 
         use-when="function-available('set:distinct')"> 
     <xsl:value-of select="set:distinct($set)" /> 
    </xsl:when> 
    <xsl:otherwise> 
     <!-- copy named template from EXSLT website --> 
     <xsl:call-template name="set:distinct"> 
      <xsl:with-param name="nodes" select="$set" /> 
     </xsl:call-template> 
    </xsl:otherwise> 
   </xsl:choose> 
  </xsl:variable> 
  <!-- more instructions --> 
 </xsl:template> 
  
</xsl:stylesheet> 

  分析:1.0 處理程序在 FC 方式下運行上述代碼,忽略所有的 use-when 屬性,但在測試的時候第一次總是失敗。如果它支持 EXSLT 包中的 set:distinct 函數則使用該函數;否則也不應該對不執行的函數拋出靜態錯誤,它將執行 xsl:otherwise 分支。2.0 處理程序必須實現 distinct-values,因此總是以其作為第一個分支。因為一些 2.0 處理程序不支持 set:distinct,使用 @use-when 篩選可以在引起靜態錯誤之前將其隱藏起來。

  避免兩個版本都不支持的擴展

  這個示例適用於希望使用 XSLT 1.0 和 2.0 中都沒有等價內置函數的擴展函數的情況。樣式表的靜態分析(和輸入文檔無關的早期錯誤檢查階段)將在所有 xsl:choose 分支中發現錯誤,包括未知的元素和未知的 XSLT/XPath 函數。未知擴展函數仍然可以通過 xsl:choose 來預防,但是只能在 BC 方式下,因此堅持使用 @use-when 篩選是一種好習慣。這個示例沒有采用 BC 方式,因此使用 exslt:power() 將在靜態分析階段被視作錯誤。不過該靜態分析發生在 @use-when 篩選之後,只對篩選後余下的部分執行。

  清單 7. 使用 XSLT 1.0 和 2.0 中都沒有等價內置函數的擴展函數

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"> 
 
<xsl:template use-when="function-available('exslt:power')" priority="10" match="a"> 
 <xsl:variable name="rolled"> 
  <xsl:choose> 
   <xsl:when test="function-available('exslt:power')"> 
    <xsl:value-of> select="trunc(6*exslt:power($x,3))+1"/> 
   </xsl:when> 
   <xsl:otherwise> 
    <xsl:call-template name="workaround-1"/> 
   </xsl:otherwise> 
  </xsl:choose> 
 </xsl:variable> 
 <!-- more instructions --> 
</xsl:template> 
 
<xsl:template version="2.0" 
       use-when="number(system-property('xsl:version')) &gt;= 2.0 and 
  not(function-available('exslt:power'))" priority="9" match="a"> 
 <xsl:variable name="rolled"> 
  <xsl:call-template name="workaround-2"/> 
 </xsl:variable> 
 <!-- more instructions --> 
</xsl:template> 
 
</xsl:stylesheet>

  2.0 樣式表函數以及 1.0 中的替代方法

  如果有一個復雜的模板,而且其可讀性或性能都比 2.0 中的函數更好,可應用下面的例子。要注意,該例使用 function-available() 而不是 @use-when 作為篩選條件。

清單 8. 將復雜的 1.0 模板改為 2.0 函數

<xsl:stylesheet version="2.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform" 
  XMLns:my="http://www.example.org/myFunctions" exclude-result-prefixes="my"> 
 
<xsl:function name="my:factorial" as="xs:integer"> 
 <xsl:param name="num" as="xs:integer" /> 
 <xsl:value-of select="if ($num > 1) then ($num * my:factorial($num - 1)) else 1" /> 
</xsl:function> 
 
<xsl:template match="/"> 
 <xsl:variable name="factorialValue"> 
  <xsl:choose> 
   <xsl:when test="function-available('my:factorial')"> 
    <xsl:value-of select="my:factorial(5)" /> 
   </xsl:when> 
   <xsl:otherwise> 
    <xsl:call-template name="factorial"> 
     <xsl:with-param name="num" select="5" /> 
    </xsl:call-template> 
   </xsl:otherwise> 
  </xsl:choose> 
 </xsl:variable> 
 <!-- more instructions --> 
</xsl:template> 
 
<xsl:template name="factorial"> 
 <xsl:param name="num" /> 
 <!-- the usual implementation goes here --> 
</xsl:template> 
 
</xsl:stylesheet> 

  分析:1.0 處理程序在 FC 方式下運行上述代碼,從而忽略 xsl:function 而不會引發錯誤。由於函數 my:factorial 不可用,因此將選擇 xsl:otherwise 分支。在 2.0 中能夠成功定義 xsl:function,因此使用 xsl:choose 的第一個分支。 提示:兩個版本中變量都采用臨時樹(1.0 規范中稱為結果樹片段)的形式,但是很可能對 $factorialValue 的後續引用中變量被原子化為數值內容。

  回退示例

  無論 1.0 還是 2.0,都可以在未知指令中嵌套 xsl:fallback 元素。(關於指令和其他 XSLT 術語的詳細解釋請參閱本系列的 第 4 部分。)如果啟用 FC 方式,較處理程序更新 XSLT 版本中的指令就是一類未知指令。比如 xsl:namespace 是 2.0 新增的用於創建名稱空間節點的指令。1.0 處理程序不能執行該指令,但是如果用以前未用過的名稱空間作為名稱創建屬性,這樣做的副作用也能創建名稱空間節點。

清單 9. 使用 Fallback 元素

<!-- The stylesheet must be versioned 2.0 for proper FC. --> 
<xsl:template name="emitNS"> 
 <!-- This template must be called before any child nodes 
    have been created for the element. --> 
 <xsl:namespace name="my" select="'http://www.example.com/NS/data'"> 
  <xsl:fallback> 
   <xsl:attribute name="my:ignore" 
     namespace="'http://www.example.com/NS/data'">junk</xsl:attribute> 
  </xsl:fallback> 
 </xsl:namespace> 
</xsl:template> 

  分析:2.0 處理程序將直接執行 xsl:namespace 指令而忽略 fallback 塊。1.0 處理程序則以 FC 方式(由於樣式表屬性 version="2.0")運行,遇到 xsl:namespace 的時候將其作為未知的 XSLT 指令來處理,就是說如果沒有 xsl:fallback 子元素的話將拋出錯誤。因為這裡有 xsl:fallback 子元素,1.0 處理程序將執行它,即執行其中的所有指令(假設是 1.0 指令)。xsl:attribute 指令將創建一個屬性節點,這不是我們所期望的,並且如果名稱的名稱空間此前沒有被聲明過就會創建一個名稱空間節點。

  使用 element-available 檢查是否支持某個指令的簡單示例

  下面的示例中,假設 1.0 處理程序可能實現了類似 2.0 xsl:result-document 元素的擴展元素(假定為 “redirect” 前綴指定了適當的名稱空間)。

清單 10. 檢查是否支持指令

<xsl:template version="2.0" match="/doc/foo"> 
 <xsl:choose> 
  <xsl:when test="element-available('xsl:result-document')"> 
   <xsl:result-document href="{@file}"> 
    <xsl:call-template name="someContent" /> 
   </xsl:result-document> 
  </xsl:when> 
  <xsl:when test="element-available('redirect:write')" 
       use-when="element-available('redirect:write')"> 
   <redirect:write select="@file"> 
    <xsl:call-template name="someContent" /> 
   </redirect:write> 
  </xsl:when> 
  <xsl:otherwise> 
   <xsl:message 
     terminate="no">Warning! Can't serialize 
      to <xsl:value-of select="@file" /></xsl:message> 
  </xsl:otherwise> 
 </xsl:choose> 
</xsl:template> 

  分析:1.0 處理程序將在 FC 方式下運行,以免在 xsl:result-document 和第二個分支中的 @use-when 上發生錯誤。第二個分支為擴展元素提供了雙重保障:@test 用於 1.0,@use-when 用於 2.0(2.0 處理程序將在早期捕捉到並刪除該分支從而避免未知指令錯誤)。所有 2.0 處理程序都必須支持 xsl:result-document,因此將執行第一個分支。(第二個分支的 @use-when 條件可以直接使用處理程序版本。)

  利用 xsl:output 中的新特性

  下面的示例允許 2.0 處理程序利用新的輸出方法 XHtml 和 xsl:output 中新的字符映射特性,同時保證如果使用 1.0 處理程序仍能保留原來的行為。(關於 xsl:character-map 的詳細介紹和示例請參閱本系列的 第 1 部分。)

清單 11. xsl:output 提供的新特性

<-- main stylesheet module --> 
<xsl:stylesheet version="2.0"> 
<xsl:import href="imp2.xsl" /> 
 
<xsl:output method="Html" 
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xHtml1-strict.dtd" 
      doctype-public="-//W3C//DTD XHtml 1.0 Strict//EN" /> 
 
<xsl:template match="/"> 
 <xsl:result-document format="output20"> 
  <xsl:fallback> 
   <xsl:call-template name="main" /> 
  </xsl:fallback> 
  <xsl:call-template name="main" /> 
 </xsl:result-document> 
</xsl:template> 
 
  ... 
 
</xsl:stylesheet> 

  下面是前面的樣式表所導入的imp2樣式表:

清單 12. Version 2.0 輸出聲明

<-- imported stylesheet module - imp2.xsl --> 
<xsl:stylesheet version="2.0"> 
 
<xsl:output method="xHtml" name="output20" use-character-maps="myMap"/> 
 
<xsl:character-map name="myMap"> 
  ... 
</xsl:character-map> 
 
</xsl:stylesheet> 

  分析:如果用 1.0 處理程序,它將合並主樣式表和導入樣式表模塊中的所有 xsl:output 聲明。在合並後的 xsl:output 中,定義了下列屬性:@method、@doctype-system、@doctype-public、@name 和 @use-character-maps。如果屬性值發生沖突(兩個樣式表都定義了 @method),具有較高導入優先級的 xsl:output 聲明中的屬性將覆蓋導入優先級低的屬性。因此,主模塊中的屬性 method="html" 將優先於導入模塊的 method="xHtml"。由於 1.0 處理程序在 FC 方式下分析合並的 xsl:output 聲明,1.0 未知的 @name 和 @use-character-maps 屬性都將被忽略。執行第一個模板的時候,因為 1.0 不支持 xsl:result-document,所以將出現回退,從而保持了 1.0 處理程序的行為。對於 2.0 處理程序,沒有 href 屬性的 xsl:result-document 指令將生成一棵隱含結果樹,就像 xsl:result-document 元素不存在一樣。但是通過添加 @format 指定它將進一步控制隱含樹的序列化,@format 指明應用哪一個(命名的)xsl:output。因而,2.0 處理程序只會調用導入 xsl:output 中使用屬性定義的序列化。

  在 1.0 和 2.0 處理程序中使用孤島

  XSLT 2.0 至少有一種特性會促使您升級。部署 2.0 處理程序的時候,只要支持 BC 特性就能處理老的代碼,當然也能處理新的 2.0 特性。(關於 2.0 可選特性的討論請參閱本系列 第 3 部分,包括 BC;關於 BC 工作原理請參閱 第 4 部分。)但是只要繼續使用 1.0 處理程序就必須安排好它,這就必然要隔離 2.0 代碼以避免錯誤,而且通常需要為 1.0 處理程序編寫備用代碼。本系列 第 2 部分 以及本文 准備工作一覽表 中都指出,一些 2.0 新增特性和其他的相比更有必要隔離到孤島中。

  可以在模板層設置 version 2.0,但是執行帶有未知屬性的 XSLT 元素時 1.0 處理程序將拋出錯誤,除非 啟用 FC 方式。為了讓 1.0 處理程序在 FC 方式下工作,需要在 1.0 處理程序認為應該設置版本號的地方設置高於 1.0 的版本號,即只有 xsl:stylesheet 外層元素或者更高層次上的 LRE。因此無論如何都需要在比模板更高的層次上設置 [xsl:]version="2.0"。與此類似,本地版本化對順序構造器或者 xsl:sort、xsl:when 這類輔助元素中的 XSLT 指令也沒有幫助。(在模板或者更低層元素上偶爾可能需要設置版本號 2.0,來告訴 2.0 處理程序不必再在 BC 方式下運行。更好的辦法是改寫代碼避免使用 BC 方式。)通過全局性設置版本號 2.0,可以讓 1.0 處理程序忽略 @use-when,這屬於一種更加前驗性的方法。

  使 2.0 指令可用

  如果您的 1.0 樣式表工作得很好,可在此基礎上建立 1.0 島。需要使用的 2.0 特性將放在 2.0 島中,但是這些島可能需要擴展到某點,該點在完成等價任務的 1.0 和 2.0 島之間具有清晰的替換關系。比方說,如果需要使用某種新的 2.0 函數,至少要將這個島擴展到一個 XPath 表達式,如果 1.0 島中有類似表達式的話。如果表達式不能替換,可能要擴展成可替換的模板或者全局 xsl:variable 元素。 清單 8 示范了這一點。

  如果要使用 1.0 已有指令的新增特性,情況就變得更復雜。首先,2.0 新增特性將被忽略,從而可能導致指令無法完成任何有意義的工作。比方說,2.0 中 xsl:attribute 使用 @select 作為替換來使用順序構造器,但是如果采用這種新的形式,1.0 處理程序根本無法知道該屬性應該具有什麼樣的值。相反,1.0 中的 xsl:value-of 使用 @select,而 2.0 允許用順序構造器代替,但是遇到這樣的子元素時 1.0 處理程序可能會拋出錯誤,不論是否處於 FC 方式。(1.0 FC 方式對已知的 XSLT 指令中出現意料之外的子元素而迷惑不解,但是一般認為位置不當的 XSLT 元素應該引起錯誤。FC 的 1.0 規范沒有具體說出現在錯誤位置的 1.0 元素不能算錯。換句話說,1.0 處理程序不能識別該位置的子元素,但是不能指望處理程序忽略它。)

  一個不那麼致命的例子是 xsl:variable 在 2.0 中允許使用 @as 屬性,聲稱是變量的特定數據類型。 1.0 處理程序將忽略 @as 並根據內置策略指定類型,但是如果需要強制轉換到特定數據類型,那麼在使用變量時,可能要取決於該數據類型。類似的,xsl:output 聲明在 2.0 中增加了新的屬性,但是在使用的時候,可能已經使用 2.0 的術語來思考了。(關於 xsl:output 和 xsl:variable 新屬性的詳細討論,以及 xsl:key 聲明新結構的兼容性的討論,請參閱 2.0 聲明一節。)

  一般而言,2.0 代碼孤島都在模板層或單獨導入的樣式表模塊中,如本文中的清單 2、3 和 7 所示。少數 2.0 指令可簡單地使用順序構造器中的 回退機制 來替代。其他多數情況下,2.0 特性的使用不能在很細的層次上明確地分離出來。比如,創建了包含原子值序列的變量之後,只有 2.0 處理程序能夠處理這樣的變量或者對其執行有意義的指令。准備工作一覽表 對隔離 2.0 代碼提供了一些建議。

  使 2.0 聲明可用

  2.0 新增聲明元素將被 1.0 處理程序忽略。(關於聲明和其他 XSLT 術語的詳細說明請參閱本系列 第 4 部分。)本文中介紹了如何解決 xsl:function 聲明的問題,如清單 3 和 8 所示。本系列 第 1 部分 討論了 xsl:character-map。1.0 處理程序不能利用通過 xsl:output 中定義的屬性啟用的新序列化特性, 但是在保留原有 1.0 功能的同時,如果處理程序能夠處理的話也可啟用 2.0 新增特性。利用 xsl:output 中的新特性 介紹了一種技術。另一種新聲明 xsl:import-schema 只能用於模式感知處理程序,一般沒有 1.0 替代技術。

  XSLT 2.0 對 xsl:key 的兩個方面做了修改,對相關的 key() 函數做了一處修改:

  1.0 中必須的 use 屬性變成了可選的,可用順序構造器代替它。這是 xsl:key 帶有子元素的第一種情況。

  值比較可以按照數據類型而不一定是字符串,對於字符串可使用指定的排序法。

  key() 的第三個可選參數指定搜索的文檔和/或子樹。

  第一種變化不會影響遺留代碼。簡言之,需要同時在 1.0 和 2.0 處理程序上運行的樣式表只能使用 @use(老辦法)定義鍵值而不能用順序構造器。第二個變化可能會造成問題,但是可以使用 BC 方式將全部比較都變成字符串比較。具體來說,可以在 xsl:key 聲明的作用域中加上 version="1.0" 以強制進行字符串比較。(仍然可以加上 @collation,1.0 處理程序將忽略,但是2.0 處理程序可采用指定的排序法進行字符串比較。)第三個變化將為習慣使用鍵的用戶帶來驚喜,因為省卻了為 key() 調用包裝 xsl:for-each 的麻煩。但是,1.0 處理程序不允許用三個參數調用 key 函數。可使用 清單 3 所示的技術將 key 聲明和調用 key() 模板的 1.0 與 2.0 版本分開。

  xsl:variable 元素在語法層次上變化很小。2.0 新增加了 as 屬性,設置變量的數據類型。如果 1.0 處理程序在 FC 方式下遇到該屬性,不應該引發錯誤,根據 1.0 規則指定該變量的數據類型。比如,下面的 2.0 版樣式表中的聲明:

<xsl:variable name="return-interval" as="xs:duration" select="'P5D'"/> 

  該例中,1.0 處理程序忽略 @as 並為該變量指定數據類型 string。但是 2.0 處理程序將為同一個變量指定 duration 類型。該變量的用法在兩個世界中是不同的,只有簡單的顯示除外(產生字符串)。如果需要 duration 類型,那麼在創建的時候就應該使用 @as,這就要用到 1.0 處理程序所不具備的能力了。

  其他多數聲明在 2.0 和 1.0 中一樣。xsl:attribute-set 的子元素新增加了一種方便的特性,但最好堅持采用 1.0 的風格。

  准備工作一覽表

  表 1 列出了修改樣式表某些具體方面的一些方法。

  表 1. 升級到 2.0 准備工作一覽表

2.0 新特性 創建 2.0 代碼 1.0 與 2.0 和平共處   xsl:character-map —— 逐字符控制輸出   更多信息請參閱本系列 第 1 部分 “輸出(序列化)方面的改進” 一節。   請參閱 清單 11 和 12.   Date/Time 處理和格式   更多信息請參閱本系列 第 1 部分 “日期/時間數據” 一節。   請參閱 清單 6。   xsl:function —— 添加樣式表函數   更多信息請參閱本系列 第 1 部分 “用戶定義函數” 一節。   請參閱 清單 8。   xsl:for-each-group —— 元素分組   更多信息請參閱本系列 第 1 部分 “分組” 一節。   如果遺留分組代碼可以明確分開,請參閱 清單 2 或 清單 3。   key() —— 使用新的三個參數   調用 key 函數之前替換必須更改上下文的代碼。   請參閱 關於鍵的說明。   @mode —— 使用關鍵字(#current、#all、#default)或 mode 列表   如果刪除重復的模板能夠簡化樣式表,則使用 mode 的改進。   對於多版本共存來說可能不是一種好辦法。   xsl:output —— 使用新的序列化選項,如:

  BOM

escape-uri-attributes

include-content-type

normalization-form

undeclare-prefixes

  XHtml

  更多信息請參閱本系列 第 1 部分 “輸出(序列化)方面的改進” 一節。   請參閱 清單 11 和 12。   xsl:result-document —— 一次生成多個結果文檔   更多信息請參閱 developerWorks 技巧 Create multiple files in XSLT 2.0。   清單 10。   模式類型 —— 使用 XML Schema 數據類型   更多信息請參閱本系列 第 1 和第 3 部分 關於模式感知的章節。   對於多版本共存可能不是一種好辦法。   新字符串函數和 xsl:analyze-string   需要的時候可用新的 2.0 函數或指令代替原來的命名模板和擴展函數。   清單 6 或 清單 9。   在 xsl:value-of 中使用順序構造器代替 select 屬性。   這是 1.0 中的必須屬性在 2.0 中變為可選屬性的兩種情況之一(另一個是 xsl:key)。可能需要順序構造器的主要原因是序列不能壓縮為一個 XPath 表達式。   如果希望 xsl:value-of 同時用於 1.0 和 2.0 處理程序,應使用 select 屬性。

  版本移植的藝術

  為了幫助您決定采用何種方式,本文詳細討論了在生產應用中保留 1.0 處理程序,同時對樣式表開始使用 2.0 新特性所帶來的後果。采用 XSLT 2.0 處理程序的用戶將獲得更好的性能和外觀,雖然他們並不知道樣式表也變得更具可讀性和更容易維護了。一旦全部 1.0 處理程序都被 2.0 處理程序所代替,就可以扔掉舊代碼和預防機制了。如果已經通過導入模塊分離開,剝離舊代碼非常簡單,比如 推薦方法 中所舉的例子。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved