關於本系列文章
W3C 發布的最新規范 XSLT 2.0 是一種轉換 XML 文檔的語言。它包括很多新的特性,部分是為克服 XSLT 1.0 的不足而設計的。在本系列文章中,我們將從希望解決老問題、學習新技術並願意了解可以從 XSLT 2.0 中期待什麼的 XSLT 1.0 用戶的角度出發,從高層概括和深入剖析兩方面考察 XSLT 2.0。為了便於升級,本文提供了源自常見應用程序的例子和實踐方面的建議。為了幫助您開始使用 XSLT 2.0,本文還提供了相應的遷移技術。
XSLT 變得越來越大越來越好
XSLT 被廣泛采納和應用於各種問題,范圍之廣超出了 XSL 工作組(WG)的預料。如果編寫過 XSLT 樣式表,您可能知道通過某些編碼模式來獲得超出代碼表面含義之外的結果。看到 generate-id() 的時候您的腦子裡會立刻想到什麼呢?是 node-set() 呢? 還是 substring-before() 呢?或是 disable-output-escaping 呢?XSLT 2.0 很快就將成為最終的推薦標准,可以用它使您的代碼更容易閱讀。新的 XSLT 特性允許您使用更接近所要表達的含義的術語。
XSLT 1.0 主要在兩個 W3C 文檔中定義:XSLT 和 XPath。2.0 版是為了適應 XQuery 的步伐而設計的,現在 XSLT 家族的核心包括六個規范:XSLT、XPath、Functions and Operators (F&O)、Data Model (XDM)、Formal Semantics 和 Serialization。與 1.0 類似,它也建立在其他幾種基礎規范之上(XML、名稱空間等),並且把 XML Schema 納入了自己的軌道。此前的需求文檔申明了要實現的新特性的目標,肯定了開發 2.0 規范的必要性。
本文以 2006 年 6 月的候選推薦標准為基礎,描述了 XSLT 2.0 中那些最有可能說服您升級到這個新版本的特性。雖然從候選推薦標准到正式推薦標准 W3C 可能會做一些細節上的修改,但是本文的大部分內容仍然適用。
分組
XSLT 1.0 中出現最早的專門技術是處理一組元素的方式,其中每個元素都有一個屬性(或後代)表明它所屬的組。1.0 規范中沒有解決元素分組。早期的 1.0 樣式表作者提煉了一種技術,雖然在代碼中不容易閱讀,但是通常可以通過 for-each 循環來識別,其形式為 <xsl:if test="generate-id(.)=generate-id(set[@grp=$thisvalue][1])">... once-per-group code ...</xsl:if>,其中比較 generate-id() 的值是為了確定當前節點是否是 $thisvalue 在 @grp 屬性中的第一個節點。如果需要篩選很大的節點集(population),這種每次使用不同的值(該例中為 $thisvalue)作為鍵的 is-it-the-first-node 方法效率可能很低。
2.0 中分組是通過新的 xsl:for-each-group 指令和兩個支持函數實現的,即 current-group() 和 current-grouping-key()。它們作用於一組類似的節點,與 xsl:for-each 以及 current() 函數非常相似。並且和 for-each一樣,for-each-group 允許使用 xsl:sort 作為子元素來控制分組處理的順序。如果熟悉 xsl:for-each,就會發現 xsl:for-each-group 用起來簡單而且自然。分組應該能夠改進性能,因為處理程序能夠優化檢測分組邊界的方式。關於分組和獲得目標結果的技術有很多內容可以寫,因此本文僅僅介紹幾種比較簡單的應用,比如對元素總體分組,這是大多數場合中的情形。
對總體分組有四種方法。其中三種方法考慮到了分組節點出現的順序,第四種采用 SQL 用戶熟悉的方式根據鍵值分組。每次使用 xsl:for-each-group 指令時,都要指定其中的一種方法:
在 group-by 方法中,需要定義一個表達式說明從何處取得總體中每個成員的鍵值(類似於 xsl:key 中的 @use,或 xsl:sort 中的 @select)。通常情況下,包含多個元素的總體根據已知的每個元素具有的屬性進行分組。假設您有組織中每個分支機構的財務數據,每個分支機構都被指定了一個地理區域。在 XML 中,數據可能采用下面這種形式:
清單 1. 需要分組的數據
<branch id="B723" region="Northeast">...data...</branch>
<branch id="B201" region="North">...data...</branch>
<branch id="B558" region="Northeast">...data...</branch>
<branch id="B064" region="West">...data...</branch>
etc.
為了將 <branch> 元素收集到區域中,可使用下面的指令: <xsl:for-each-group select="branch" group-by="@region">... once-per-region code...</xsl:for-each-group>。在循環體中,current-grouping-key() 保存了當前的 @region 值,可以使用 <xsl:for-each select="current-group()">...once-per-branch code...</xsl:for-each> 迭代當前的分組,即同一個區域中的所有分支機構。要注意,不需要建立分組就能很容易得到所有 grouping-key 值(該例中為區域名稱)的列表,只要使用新的 distinct-values() 函數即可。關於 distinct-values 函數的更多信息,請閱讀文章“XML for Data: What's new in XPath 2.0?”。
group-by 方法不關心節點的順序。它允許多個鍵,提高了一個分組中重復節點的可能性。規則是在任何給定的分組中,每個成員節點只能出現一次。但是,同一個節點可以是多個組的成員。
設計 group-ending-with 方法是考慮到按照一定順序排列、並且帶有標志 節點(通常表示一個分組結束)的元素總體。比方說在數字應用程序中,經常在列表中遇到 <subtotal>、<total> 和 <grand-total> 這樣的標志。在 xsl:for-each-group 中,@group-ending-with 使用 XSLT 范式語法指定如何識別標志節點。用 XSLT 1.0 實現相同的功能可能非常麻煩,需要結合使用 position()、遞歸處理、preceding-sibling 軸,而且可能需要分為兩個階段來處理。
group-starting-with 方法與 group-ending-with 類似,但標志節點出現在分組的開始。在處理莎士比亞戲劇的 XML 版本時可使用這種形式的分組(這種版本已經在 Web 上存在很多年了)。<speech> 和 <line> 元素按照順序出現。說話的角色用行前面單獨的元素表示,一個場景中在需要的時候會出現 <StageDir> 元素,但遵循一定的順序。在某些應用中,可能希望某些兄弟元素是嵌套的或者相反,但是分組可以克服這些問題。一種可能是標志 <StageDir> 元素在某一劇情有效的時候對所有其他元素分組。
第四種分組方法是 group-adjacent。與 group-by 類似,它也使用節點值而不是范式來劃分組,不過它能夠識別總體中元素的順序。因此,它用於監視每個元素都有的某個節點(屬性或後代),當該節點的值不同於上一個值時,則劃分出一個組。它可用於劃分固定大小的分組,使用下面這種很快就會盛行的技術:
<xsl:for-each-group select="item" group-adjacent="ceiling(position() div 4)">
它把 <item> 元素每四個分成一組。
順便說一下,XSLT 2.0 提供了一些運算符可以更方便地檢查節點順序或者節點相等。這一節開始使用的 generate-id() 實際上是判斷兩個路徑是否指向同一個節點。在 2.0 中為此提供了一個運算符 is。類似地,>> 和 << 運算符用於檢查節點的文檔順序在另一個之後還是之前。這些運算符可以簡化 xsl:for-each-group 不能解決的任何可以預見的需要。
關於分組還有很多其他技巧和技術。這一節只是讓您對其可能性有一個總體了解。
隱含文檔節點
XSLT 1.0 受到挫折的主要原因之一是結果樹片段(Result Tree Fragment,RTF)的局限性。雖然表示樹的變量可以復制到結果集或者將其看作一個字符串,但是不能使用標准 XSLT/XPath 工具像樹那樣導航。也許和其他很多人一樣,您曾經反復琢磨 11.1 節中的這樣一段話,“結果樹片段被視作相當於只包含一個根節點的節點集。但是結果樹片段支持的操作是節點集所允許的操作的一個子集。僅當一個操作能用於字符串時才允許用於結果樹片段……” 讀到這裡,您可能奇怪為什麼它不能作為樹用 XPath 表達式導航。
多數 XSLT 處理程序的提供商都增加了一個擴展函數,通常稱為 node-set(),它可以把 RTF 變成可導航、可篩選的樹。還記得在 1.0 中,xsl:variable 可以有屬性 @select,它的值可以是一個節點集或者單個原子節點,或者以內容構造器作為子元素來創建 RTF。後一種形式建立了 2.0 中的隱含文檔節點(Implicit Document Node,IDN),這是一種新增加的臨時樹 結構。如果 1.0 樣式表創建了 RTF 變量,2.0 版相同的樣式表將創建 IDN。如果需要 xsl:choose 或 xsl:call-template 這樣的編程指令,xsl:variable 中仍然需要內容構造器(2.0 中重新命名為序列構造器),從而迫使您創建 RTF/IDN 樹。與以前一樣,可以使用 xsl:copy-of 將整個 IDN 樹復制到輸出,但是也可在路徑表達式中使用該變量而不需要轉換成節點集。
IDN 或其子樹可以出現在 xsl:apply-templates 的 select 表達式中,允許按照與 XML 輸入文檔相同的方式處理。如果很大,這是一種兩階段轉換的形式,但是不需要將處理程序的運行劃分成階段 1 和階段 2。如果需要構造帶有大量交叉引用的 XML 文檔,則劃分為兩個或更多階段比較方便。不用節點集函數避免了出現樹的兩個副本的弊端,一個 RTF 和一個節點集容易造成混亂。對於很大的臨時樹,這還有助於節約內存。如果構造 IDN 時建立了 ID 類型的屬性或者適當的鍵值,可以使用 id() 和 key() 函數進行索引訪問,這些函數的 2.0 版還增加了一個方便的參數,以限制檢索到目標樹中的索引。
IDN 變量引用可以後跟路徑步(如 /child)和篩選器(如 [@foo="yes"]),從而為 xsl:for-each 循環選擇遍歷的節點集,或者在其他 select 表達式中使用的地址值。同樣也能應用於 xsl:if 和 xsl:when 中的 test 表達式。
注意,xsl:variable 提供了控制數據類型化的選項。因而,xsl:variable 能夠與序列構造器內容結合在一起代替 @select,不用創建完整的 IDN,而是創建獨立的屬性節點、注釋和處理指令。它可以創建獨立的包含內容的元素,與 IDN 不同的是,這些元素在樹的頂端沒有文檔節點。它也可以創建原子值序列,這有可能與結點集混淆。下面的例子說明了如何使用 @as 避免創建文檔節點和任何元素,而是創建一個序列。
清單 2. 用 @as 修飾的變量作為 xs:double 值的序列,沒有文檔或元素節點
<xsl:variable name="values" as="xs:double*">
<xsl:sequence select="(1,3,5)"/>
<xsl:if test="@m='large'">
<xsl:sequence select="(2,4,10)"/>
</xsl:if>
</xsl:variable>
變量 $values 既不是節點集也不是 IDN,僅僅是一個原子性的雙精度數序列。(注意,本文中的名稱空間前綴 “xs” 指向 XML Schema 數據類型名稱空間。)
用戶定義函數
XSLT 2.0 引入的新指令 xsl:function 允許樣式表作者使用純 XSLT 語言和語義在樣式表中創建自己的函數。當然,只有在 F&O 和 XSLT 2.0 規范所提供的 128 個函數和 68 個運算符中沒有找到需要的函數時才需要這樣做。xsl:function 的例子請參閱後面 字符映射 一節。
由於不能方便地創建用戶定義函數,XSLT 1.0 樣式表作者常常采用其他方法來達到目的,比如調用命名模板、用其他語言定義自己的擴展函數、或者希望 XSLT 1.0 處理程序支持偽標准的擴展庫,如 EXSLT。F&O 庫中的一些函數和 EXSLT 中定義的函數重疊,但是兩者都提供了對方所不具備的函數。EXSLT 也定義了擴展指令 func:function,語法與 xsl:function 相同(如果使用過 func:function,采用 XSLT 2.0 的時候應該改過來)。使用 EXSLT 擴展的不利之處在於有些實現僅提供了有選擇地支持,而且不能保證將來繼續支持這些擴展。
用其他語言編寫自己的擴展函數通常費時費力。您的擴展函數實現可能僅限於某一個處理程序,而且總是受到那個處理程序所公開的機制和數據結構的限制。在 XSLT 1.0 中,編寫命名模板來獲得需要的功能是最安全的方法,沒有依賴於特定實現的局限性。但是調用命名模板需要更多的 XSLT 指令,調用函數則更緊湊而且很容易嵌入到 XPath 表達式中。另一方面的局限與從 xsl:variable 內容中調用命名模板時的結果樹片段有關(參閱上一節 隱含文檔節點)。如果由於需要某個擴展函數實現,1.0 樣式表不能在 2.0 處理程序上運行,首先應檢查 F&O 和 XSLT 2.0 規范,看看有沒有合適的函數。如果沒有找到需要的函數,可以嘗試在樣式表中編寫自己的函數。對部分但不是全部 EXSLT 函數,EXSLT 提供了基本等價的命名模版,可以此為指導用 XSLT 語法編寫自己的函數(要記住 EXSLT 是為 XSLT 1.0 提供的,因此使用新的 2.0 語法可以進一步簡化函數)。如果發現擴展函數比樣式表函數更好,可以定義樣式表函數作為擴展函數不能使用時的第二選擇。
日期/時間數據
XSLT 2.0 標准家族提供了對日期/時間數據的支持,包括一組函數和運算符,可執行與時間有關的計算。不再需要將日期和時間作為字符串來處理,提取部分字符串作為單個數字或者月份的名稱。該數據類型來自 XML Schema Part 2 規范,它提供了時間點和時期類型。如果啟用了模式感知,這些類型的值可以來自轉換的輸入文檔或者函數的返回值。一些 XSLT 2.0 指令提供了斷言數據類型的機制,比如新增的 @as 屬性。
如果需要創建與時間相關的數據類型的單個值,可以使用類型構造器函數並提供時間的字符串表示形式作為參數。比如,xs:time('12:34:56') 接受時間的字符串表示形式,並返回一個類型注釋被標記為一天中某個時刻的值。這類構造器函數的名稱與 XML Schema 的內置類型名相同:xs:date、xs:time、xs:dateTime、xs:duration、xs:gYear、xs:gMonth、xs:gDay、xs:gYearMonth、xs:gMonthDay,並增加了 xs:yearMonthDuration 和 xs:dayTimeDuration。
得到表示為一天中某個時刻的值 12:34:56 後,可以加上一個時期值來得到稍後的一個時刻。提供了很多新的函數和運算符在時間點和時期之間完成適當的算術運算。可以用時期值乘上一個標量值得到更長或更短的時期值,也可以除一個標量值或者另一個時期。兩個 dateTime 相減的差是一個時期值。類型相同的兩個值可以比較。比如,PT90S(90秒)的 dayTimeDuration 與規范化值 PT1M30S(1 分鐘 30 秒)進行比較是相等的。有些類型還定義了不等於關系。針對時期的大於表示更長的時期;針對日期的大於表示較晚的日期。
此外,還有一組名稱淺顯易懂的分量抽取 函數,只要一個操作就能從日期、時間或時期數據中抽取一個字段。比如,
minutes-from-dateTime(xs:dateTime('2005-07-01T14:06:32'))
返回整數 6,而在過去要取出分鐘數,需要混合使用 substring-before() 和 substring-after() 函數,還可能要用到 number()。在新方法中,函數名清楚地表明了要做什麼。
在 1.0 樣式表中如果要大量處理時間數據,可能需要執行一些調用專門命名模板的操作。如果模板的返回值需要提供給 XPath 表達式時,這樣做很不便,因為需要用 xsl:variable 將模板調用包圍起來,該變量是一個 RTF。一般來說,xsl:function是 2.0 中采用的方法,但是對於時間相關數據,有 24 個新函數和 44 個新運算符再加上構造器函數可供選擇。相對而言,一些 XSLT 1.0 處理程序提供的 EXSLT 包只有 28 個與時間有關的函數,包括一個格式化函數。
根據用戶廣泛的要求,新函數集也提供了返回當前時間、日期或者 dateTime 的函數,因此樣式表可以在輸出中打上時間戳。
上述都是按照對應的實際類型處理的情況。這些函數是 XSLT 和 XQuery 共用的。因為 XSLT 還承擔著生成可表示信息的使命,XSLT 增加了接受 date、time 或 dateTime 類型值並生成帶有所需修飾的字符串的函數,比如月份名稱的拼寫。XSLT 2.0 規范允許實現選擇不同的語言、數字等類似的方面。如果這些方面很重要,現在就可以考慮組成需要的列表,在規范完成之後檢查 2.0 處理程序是否有自己需要的特性。
模式感知
XSLT 2.0 中模式感知是一種可選特性。如果處理程序支持它,可以發現這是一種有用的特性。模式感知特性主要用於錯誤檢查。它可以用 XML 模式驗證輸入和輸出,允許根據 Schema 類型引用樣式表中的源節點。對於 1.0 樣式表作者而言,該特性可以在調試受挫之前預先發現錯誤。可通過下列機制利用這一特性:
表 1. 支持模式感知特性的指令屬性和 XPath 成分
語法項 用法 @as 屬性 在 xsl:variable、xsl:param、xsl:function 或 xsl:template 上使用該屬性來斷言生成的結果樹或序列是目標 Schema 類型的實例。 instance of 對序列、單個原子值或節點使用該運算符來檢驗是否為目標 Schema 類型的實例。 @validation 和 @type 屬性[對於 Literal Result Element (LRE) 則為 @xsl:validation 和 @xsl:type] 使用這些屬性根據 Schema 類型定義或者頂層元素和屬性定義來驗證節點。 cast as 和構造器函數 將原子類型強制轉換成 Schema 類型。 castable as 檢查能否進行強制類型轉換的運算符。 node-test element(*,type) 和 attribute(*,type) 僅返回標記為目標 Schema 類型的元素或屬性節點。 node-test schema-element(elemDecl) 和 schema-attribute(attribDecl) 返回與 Schema 元素或屬性定義匹配的元素或屬性節點。輸出(序列化)的改進
xsl:result-document
一個 2.0 樣式表可以生成多棵結果樹,在不同的位置進行序列化。developerWorks 文章 “Create multiple files in XSLT 2.0”就如何使用新的 xsl:result-document 指令做了一般性介紹。
字符映射
1.0 樣式表作者常常錯誤地使用 disable-output-escaping(d- o-e)屬性,沒有充分理解標記和文本之間的區別。這也是體系結構上的一個缺陷,因為它要求序列化程序除了知道 XPath 數據模型和 Serialization 參數公開的信息之外,還要知道每個節點的附加字段。這樣就模糊了轉換和序列化之間的界限,因此 XSL WG 不贊成使用該屬性。他們在 2.0 中引入了字符映射,這是在序列化過程中進行的字符映射,出現的特定字符都要被映射成 use-character-maps Serialization 參數所要求的另一個字符,即便造成 XML 不再是格式良好的。下面的樣式表說明了如何使用字符映射,用 my:d-o-e 函數替換 @disable-output-escaping。也可使用該函數作為一個例子,僅對結果樹中選定節點使用字符映射。這個例子依賴於映射不常用的字符,如左尖引號(«,通常編碼為 «),並假設這樣的字符不出現在結果序列中。如果需要保證只有目標字符被映射,可以使用更少見的字符。
清單 3. 在輸出中得到特殊 XML 字符的更好辦法
<xsl:output use-character-maps="my:charMap" />
<xsl:character-map name="my:charMap">
<xsl:output-character character="«" string="<" />
<xsl:output-character character="»" string=">" />
<xsl:output-character character="§" string="&" />
<xsl:output-character character="¤" string="'" />
<xsl:output-character character="¦" string=""" />
</xsl:character-map>
<xsl:function name="my:d-o-e" as="xs:string">
<xsl:param name="str" as="xs:string" />
<xsl:sequence select="translate($str, '<>&'"',
'«»§¤¦')" />
</xsl:function>
<xsl:template match="/doc">
Replace
<xsl:value-of select="." />
With
<xsl:value-of select="my:d-o-e (.)"/>
</xsl:template>
使用下列字符映射避免轉義所有的與符號(&)、小於號(<)、大於號(>)、單引號(')和雙引號(")字符。雖然似乎僅僅是把每個字符映射為自身,它實際上是避免了 Serialization 中的轉義步驟(根據 XML 或 Html 規則),因為該步驟不能應用於被映射的字符本身。
清單 4. 保留特殊 XML 字符為非轉義形式的另一種方法
<xsl:character-map name="my:charMap">
<xsl:output-character character="<" string="<" />
<xsl:output-character character=">" string=">" />
<xsl:output-character character="&" string="&" />
<xsl:output-character character="'" string="'" />
<xsl:output-character character=""" string=""" />
</xsl:character-map>
Unicode 規范化
Unicode 規范化是 XSLT 用戶的要求,結果 XSLT 2.0 提供了兩種執行規范化的選擇。要實現選擇性規范化,可使用 F&O 函數 normalize-unicode()。另外,Serialization 參數 normalization-form 將影響到整個最終結果樹的規范化。如果使用字符映射,則首先考慮是否對映射字符進行規范化,還是對映射後的字符規范化。第一種情況應使用 normalize-unicode(),後者則使用參數 normalization-form。
URI 轉義的三個函數
F&O 規范定義了三個函數:encode-for-uri()、iri-to-uri() 和 escape-html-uri(),來執行構成 URI 或其一部分的字符串的百分號編碼。在 URI 中,可以通過用百分號後面將兩位十六進制數字替換實際字符來轉義具有語法意義的字符,比如 %20 表示空格。EXSLT 定義的 str:encode-uri() 也執行百分號編碼,但是與 encode-for-uri() 相比少了 5 個保留字符。根據需要,F&O 中的這三個函數應該足夠了。為了控制 Html 輸出中轉義 URI 屬性,可使用 escape-uri-attributes Serialization 參數。該參數不能用於 XSLT 1.0,因此 URI 轉義是不可避免的。
下面是 US-ASCII 編碼字符集中這些函數將采用百分號編碼的的可打印字符列表,碼值為 32 到 126(十進制):
fn:iri-to-uri() 編碼 " < > \ ^ ` { | } space
EXSLT 的 str:encode-uri() 編碼 " # $ % & + , / : ; < = > ? @ [ \ ] ^ ` { | } space
fn:encode-for-uri() 編碼 ! " # $ % & ' ( ) * + , / : ; < = > ? @ [ \ ] ^ ` { | } space
fn:escape-Html-uri() 不對這些字符編碼
創建名稱空間節點
新增的 xsl:namespace 指令顯然是用於創建名稱空間節點的簡單方法,解決了 XSLT 1.0 缺少這一機制的問題。在 XSLT 1.0 中,天真的用戶可能曾經嘗試用 xsl:attribute 並以 “XMLns:prefix” 作為其名稱來創建名稱空間。名稱空間節點實際上只能間接創建,要麼從源文檔中復制元素或者名稱空間節點,如果實例化了 LRE,則可以從樣式表中復制名稱空間,要麼直接創建其名稱在名稱空間中的元素或屬性。換句話說,在 1.0 中,您要麼使用已經創建的名稱空間,要麼創建其他不需要的節點作為副本來生成名稱空間節點。
XML 1.1(前綴未聲明和 unicode 規范化)
可以用符合 XSLT 1.0 的處理程序作為最後修正版本來支持 XML 1.1。XML 1.1 對於 XSLT 1.0 和 2.0 都是可選的。但是如果支持,仍然要明確輸出屬性 normalization-form 和 undeclare-prefixes 只能用於 XSLT 2.0,而不能用於 XSLT 1.0。
XHtml 輸出方法
XSLT 2.0 增加了一種新的輸出方法 XHTML,所有符合 Serialization 規范的處理程序都必須支持它。在 XSLT 1.0 中也能序列化為 XHTML,只要輸出為 XML 並設置 xsl:output 的 doctype-system 和 doctype-public 屬性即可。這樣可以保證 <!DOCTYPE> 聲明被序列化。但是,由於 XHTML 規范 Appendix C 中列出的細微差別,對於某些設計用於處理 Html 的代理來說,這樣可能還不夠。
xsl:value-of 的分隔符屬性
可能最受青睐的 XSLT 1.0 指令是 xsl:value-of,用於把值作為字符串放到輸出中。很多樣式表作者使用它作為調試工具,但是又發現如果不清楚 expr 有多少節點,還是必須使用 count(expr)。在 2.0 中,xsl:value-of 不僅僅輸出集合中的第一個成員,實際上遍歷整個序列,甚至可以控制 separator 字符串(如果需要 xsl:value-of 僅返回第一個成員,只要 2.0 處理程序支持向後兼容,可以在指令中設置 version="1.0")。<xsl:value-of select="/doc/*" separator="', '"/> 這行簡單的指令將替換下面所有的代碼:
清單 5. 格式化值列表的典型 1.0 代碼,2.0 中只要一條指令就能完成
<xsl:template match="/doc">
<xsl:call-template name="valueOfWithSeparator">
<xsl:with-param name="nodes" select="*" />
<xsl:with-param name="separator" select="', '" />
</xsl:call-template>
</xsl:template>
<xsl:template name="valueOfWithSeparator">
<xsl:param name="nodes" />
<xsl:param name="separator" select="' '" />
<xsl:for-each select="$nodes">
<xsl:if test="not(position() = 1)">
<xsl:value-of select="$separator" />
</xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
字節順序標志以
為了控制樣式表中的字節順序標志(Byte-order-mark,BOM),可以使用 byte-order-mark Serialization 參數。要注意,允許實現者覆蓋這個值。在 XSLT 1.0 中,實現可以在 UTF-8 文檔中加上 BOM,UTF-16 和 UTF-32 可能也是如此。在 XSLT 1.0 中,要麼實現者必須支持擴展屬性,要麼用戶必須進行外部(轉換以後)處理,以保證增加或去掉了 BOM。
結束語
本文描述了 XSLT 2.0 中最為迫切要求的主要特性,XPath 和函數庫留待以後再討論。如果還沒有看到讓您心甘情願地升級到 2.0 的特性,請繼續關注後續文章。