XSLT是什麼類型的語言,其用途是什麼,爲什麼要這樣設計它?這些問題可以有許多不同的答案,初學者往往會感到困惑,因爲這種語言與他們以前習慣使用的語言之間有很大差別。本文嘗試說明XSLT。本文並不試圖教您編寫 XSLT樣式表,它將說明這種語言的起源,它擅長什麼,以及您爲什麼應該使用它。
我撰寫本文的初衷是爲一篇關於 Saxon 的技術文章提供必要的背景知識,打算提供在傳統 XSLT 處理器中使用的實現技巧內幕,從而幫助用戶使其樣式表的性能達到最大化。但 developerWorks 的編輯們勸說我:這篇介紹應該吸引更廣泛的讀者,值得作爲 XSLT 語言的獨立說明而單獨發表。
XSLT 語言由萬維網聯盟 (W3C) 定義,並且該語言的 1.0 版本在 1999 年 11 月 16 日作爲“推薦書”發佈(請參閱 參考資料)。我已經在拙作 XSLT Programmers' Reference 中提供了全面的規範和用戶指南,因此我不打算在本文中涵蓋相同內容。確切地講,本文的目的只是使讀者理解 XSLT 適合大規模事物的哪些位置。
|
XSLT 的最初目的是將信息內容與 Web 顯示分離。如其最初定義那樣,HTML 通過按抽象概念(如段落、重點和編號列表)定義顯示來實現設備獨立性。隨着 Web 變得越來越商業化,出版人希望其輸出質量能達到與印刷品相同的質量。這逐漸導致越來越多地使用具體顯示控件,如頁面上材料的明確字體和絕對位置。然而不幸的是完全可以預料其副作用,即將相同的內容傳遞到替代設備,如數字電視機和 WAP 電話(印刷業的行話 再現效果)將會變得日益困難。
由於吸收了印刷業使用 SGML 的經驗,在 1998 年初定義了一種標記語言 XML,它用於表示獨立於顯示的結構化內容。與 HTML 使用一組固定概念(如段落、列表和表)不同,XML 標記中使用的標記完全是用戶定義的,其用意是這些標記應該與所關注的對象(如人、地點、價格和日期)相關。儘管 HTML 中的元素本質上都是印刷樣式(雖然處於抽象級別),而 XML 的目標是元素應該描述實際對象。例如,清單 1 顯示了表示足球錦標賽結果的 XML 文檔。
清單 1. 表示足球錦標賽結果的 XML 文檔
<results group="A"> <match> <date>10-Jun-1998</date> <team score="2">Brazil</team> <team score="1">Scotland</team> </match> <match> <date>10-Jun-1998</date> <team score="2">Morocco</team> <team score="2">Norway</team> </match> <match> <date>16-Jun-1998</date> <team score="1">Scotland</team> <team score="1">Norway</team> </match> <match> <date>16-Jun-1998</date> <team score="3">Brazil</team> <team score="0">Morocco</team> </match> <match> <date>23-Jun-1998</date> <team score="1">Brazil</team> <team score="2">Norway</team> </match> <match> <date>23-Jun-1998</date> <team score="0">Scotland</team> <team score="3">Morocco</team> </match> </results> |
如果要通過 Web 瀏覽器顯示這些足球賽的結果,不要指望系統會產生合理的佈局。需要其它一些機制來告訴系統如何在瀏覽器屏幕、電視機、WAP 電話或真正在紙張上顯示數據。這就是使用樣式表的目的。樣式表是一組說明性的規則,它定義了應如何表示源文檔中標記標識的信息元素。
W3C 已經定義了兩個系列的樣式表標準。第一個是在 HTML 中廣泛使用的 CSS(級聯樣式表),當然它也可以在 XML 中使用。例如,可以使用 CSS 來表示何時顯示發票,應支付的總額應該用 16 點 Helvetica 粗體字顯示。但是,CSS 不能執行計算、重新整理或排序數據、組合多個源碼中的數據或根據用戶或會話的特徵個性化顯示的內容。在這個足球賽結果的例子中,CSS 語言(即使是最新版本 CSS2,尚未在產品中完全實現)的功能還不夠強大,不能處理這項任務。由於這些原因,W3C 已着手開發更強大的樣式表語言 XSL(可擴展樣式表語言),並採納了 SGML 社區中開發的 DSSSL(文檔樣式、語義和規範語言)中許多好的構思。
在 XSL 的開發過程中(這在 DSSSL 中已有所預示),發現在準備 XML 文檔以備顯示的過程中執行的任務可以分成兩個階段:轉換和格式化。轉換是將一個 XML 文檔(或其內存中的表示法)轉換成另一個 XML 文檔的過程。格式是將已轉換的樹狀結構轉換成兩維圖形表示法或可能是一維音頻流的過程。XSLT 是爲控制第一階段“轉換”而開發的語言。第二階段“格式化”的開發工作還是進行中。但實際上,大多數人現在使用 XSL 將 XML 文檔轉換成 HTML,並使用 HTML 瀏覽器作爲格式化引擎。這是可行的,因爲 HTML 實際上只是 XML 詞彙表的一個示例,而 XSLT 可以使用任何 XML 詞彙表作爲其目標。
將轉換成一種語言和格式化成另一種語言這兩個操作分離經證實的確是一種好的決策,因爲轉換語言的許多應用程序經證明無法向用戶顯示文檔。隨着 XML 日益廣泛地用作電子商務中的數據互換語法,對於應用程序將數據從一個 XML 詞彙錶轉換成另一個 XML 詞彙表的需求也在不斷增加。例如,某個應用程序可能從電視收視指南中抽取電視節目的細節,並將它們插入按次付費客戶的月帳單中。同樣,還有許多實用的數據轉換,在這些轉換中源詞彙表和目標詞彙表是相同的。它們包括數據過濾,以及商務操作,如施行漲價。因此,隨着在系統中開始越來越多地以 XML 語法的形式使用數據,XSLT 就逐漸成爲由於處理這些數據的隨處可見的高級語言。
在拙作中,我做了這樣一個比喻:XSLT 與 XML 的關係,就好象 SQL 與表格化數據的關係一樣。關係模型的強大功能並非來自用表存儲數據的思想,而是源於 SQL 中可行的基於關係運算的高級數據操作。同樣,XML 的層次化數據模型對應用程序開發者的幫助實際上也非常小。正是因爲 XSLT 作爲 XML 數據的高級操作語言提供瞭如此強大的功能。
|
就某些方面而言,XSLT 作爲一種語言來說是非常古怪的。我不打算在本文中討論已做出的設計決策的基本原理,儘管可以通過它們在邏輯上追溯到語言設計者確定的對 XSLT 的要求。如需更完整的說明,請參閱拙作的第 1 章。
以下概述了 XSLT 語言的部分主要特性。
XSLT 樣式表是一個 XML 文檔 。通過使用 XML 的尖括號標記語法來表示文檔的結構。這種語法在某種程度上是比較笨拙的,而此決策可以使該語言變得更羅嗦。但是,它確實有好處。它表示可以自動使用 XML 的所有詞彙設備(例如,Unicode 字符編碼和轉義,使用外部實體等等)。它表示很容易使 XSLT 樣式表變成轉換的輸入或輸出,使該語言可以作用於自身。它還使將期望的 XML 輸出塊嵌入樣式表變得很容易。實際上,許多簡單的樣式表基本上可以寫作期望輸出文檔的模板,並且可以將一些特殊指令嵌入文本中,以便插入輸入中的變量數據或計算某個值。這就使 XSLT 在這個簡單的級別上非常類似於許多現有的專用 HTML 模板語言。
基本處理範例是模式匹配。 在這方面,XSLT 繼承了文本處理語言(如 Perl)的傳統,這種傳統可以一直追溯到 1960 年代的語言,如 SNOBOL。XSLT 樣式表包括一組模板規則,每條規則都使用以下方式:“如果在輸入中遇到此條件,則生成下列輸出。”規則的順序是無關緊要的,當有幾條規則匹配同一個輸入時,將應用衝突解決算法。然而,XSLT 與串行文本處理語言的不同之處是 XSLT 對輸入並非逐行進行處理。實際上,XSLT 將輸入 XML 文檔視爲樹狀結構,每條模板規則都適用於樹中的一個節點。模板規則本身可以決定下一步處理哪些節點,因此不必按輸入文檔的原始順序來掃描輸入。
|
XSLT 處理器使用樹狀結構作爲其輸入,並生成另一個樹狀結構作爲輸出。圖 1 中顯示了這一點。
圖 1. XSLT 輸入和輸出的樹狀結構
常常通過對 XML 文檔進行語法分析來生成輸入樹狀結構,而輸出樹狀結構通常被串行化到另一個 XML 文檔中。但 XSLT 處理器本身操作的是樹狀結構,而不是 XML 字符流。這個概念最初給許多用戶的感覺是不切實際的,結果卻對理解如何執行更復雜的轉換起了關鍵作用。首先,它表示 XSLT 處理器可以理解源文檔中與樹狀結構無關的特殊之處。例如,無論屬性是包括在單引號中還是在雙引號中,都不可能應用不同的處理,因爲會將這兩種形式視爲同一個基本文檔的不同表示方法。更深入地看,它表示處理輸入元素或生成輸出元素是一個原子操作。不可能將處理元素的開始標記和結束標記分成單獨的操作,因爲一個元素會自動錶示成樹模型的單節點。
XSLT 使用叫作 XPath 的子語言來引用輸入樹中的節點。XPath 本質上是與具有層次結構的 XML 數據模型相匹配的查詢語言。它可以通過按任何方向瀏覽樹來選擇節點,並根據節點的值和位置應用謂詞。它還包括用於基本字符串處理、數字計算和布爾代數的工具。例如,XPath 表達式 ../@title
選擇當前節點的父代元素的標題屬性。XPath 表達式用於選擇要進行處理的輸入節點、在條件處理期間測試條件,以及計算值以便插入結果樹中。模板規則中還使用了 XPath 表達式的簡化形式“模式”來定義特定模板規則適用於哪些節點。XPath 在單獨的 W3C 推薦書中定義,它允許使用在其它上下文中再使用的查詢語言,特別是用於定義擴展超鏈接的 XPointer。
XSLT 以傳統語言(如 Lisp、Haskell 和 Scheme)中的功能性編程的概念爲基礎。樣式表由模板組成,這些模板基本上是單一功能 -- 每個模板將輸出樹的一部分定義成一部分輸入樹的功能,並且不產生副作用。使用無副作用的規則受到嚴格控制(除了轉義成用類似 Java 的語言編寫的外部代碼)。XSLT 語言允許定義變量,但不允許現有變量更改它的值 -- 即沒有賦值語句。這個策略使許多新用戶感到困惑,其目的是爲了允許逐步應用樣式表。其原理是如果語言沒有副作用,那麼對輸入文檔做很小的改動時,不必從頭執行整個轉換就應該可以計算出對輸出文檔的最後更改。目前必須說這只是理論上的可能,任何現有 XSLT 處理器還不能實現。(注:雖然 XSLT 以功能性編程概念爲基礎,但它還不是一個完整的功能性編程語言,因爲它缺少將函數當作一級數據類型進行處理的能力。)
|
在這個階段,使用示例會使語言變得更清楚。清單 2 顯示了列出足球賽結果的簡單樣式表。
清單 2. 足球賽結果的基本樣式表
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="results"> <html> <head><title> Results of Group <xsl:value-of select="@group"> </title></head> <body><h1> Results of Group <xsl:value-of select="@group"> </h1> <xsl:apply-templates> </body></html> </xsl:template> <xsl:template match="match"> <h2> <xsl:value-of select="team[1]"> versus <xsl:value-of select="team[2]"> </h2> <p>Played on <xsl:value-of select="date"></p> <p>Result: <xsl:value-of select="team[1] "> <xsl:value-of select="team[1]/@score">, <xsl:value-of select="team[2] "> <xsl:value-of select="team[2]/@score"> </p> </xsl:template> </xsl:transform> |
這個樣式表包括兩個模板規則,一個匹配 <results>
元素,另一個匹配 <match>
元素。 <results>
元素的模板規則輸出頁面的標題,然後調用 <xsl:apply-templates>
,這是一個 XSLT 指令,它將處理當前元素的所有子代,對於每個子代都使用其適當的模板規則。在本例中, <results>
元素的所有子代都是 <match>
元素,所以會用第二個模板規則來處理它們。規則輸出了一個標識比賽的次級 HTML 標題(以 "Brazil versus Scotland" 的形式),然後生成 HTML 段落,給出了比賽的日期和兩隊的比分。
該轉換的結果就是一個 HTML 文檔,該文檔在瀏覽器中的表示如圖 2 所示。
圖 2. 清單 2 中樣式表的結果
這是一種非常簡單的表示信息的方法。然而,XSLT 的功能比這要強大得多。清單 3 包含了另一個可以操作相同源數據的樣式表。這次,樣式表計算一個比賽名次表,用來顯示錦標賽結束時各隊的名次。
清單3. 計算球隊名次表的樣式表
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:variable name="teams" select="//team[not(.=preceding::team)]"> <xsl:variable name="matches" select="//match"> <xsl:template match="results"> <html><body> <h1>Results of Group <xsl:value-of select="@group"></h1> <table cellpadding="5"> <tr> <td>Team</td> <td>Played</td> <td>Won</td> <td>Drawn</td> <td>Lost</td> <td>For</td> <td>Against</td> </tr> <xsl:for-each select="$teams"> <xsl:variable name="this" select="."> <xsl:variable name="played" select="count($matches[team=$this])"> <xsl:variable name="won" select="count($matches[team[.=$this]/@score > team[.!=$this]/@score])"> <xsl:variable name="lost" select="count($matches[team[.=$this]/@score < team[.!=$this]/@score])"> <xsl:variable name="drawn" select="count($matches[team[.=$this]/@score = team[.!=$this]/@score])"> <xsl:variable name="for" select="sum($matches/team[.=current()]/@score)"> <xsl:variable name="against" select="sum($matches[team=current()]/team/@score) - $for"> <tr> <td><xsl:value-of select="."></td> <td><xsl:value-of select="$played"></td> <td><xsl:value-of select="$won"></td> <td><xsl:value-of select="$drawn"></td> <td><xsl:value-of select="$lost"></td> <td><xsl:value-of select="$for"></td> <td><xsl:value-of select="$against"></td> </tr> </xsl:for-each> </table> </body></html> </xsl:template> </xsl:transform> |
這裏沒有足夠的篇幅來完整地說明這個樣式表,簡而言之,它爲球隊聲明瞭一個變量,變量值是一個節點集合,其中每個參賽球隊都有一個實例。然後它計算每支球隊的勝、平或負的比賽場次總數,以及球隊進球或失球的總數。圖 3 顯示了它在瀏覽器中的最終輸出結果。
圖 3. 清單 3 中名次樣式表的結果
這個示例的目的是說明 XSLT 不單單能夠對源文檔中出現的文本指定字體和佈局。它是一個完整的編程語言,能夠以任何方式轉換源數據以供顯示,或者輸入另一個應用程序。
|
您爲什麼考慮使用 XSLT?
XSLT 給了您傳統高級聲明編程語言的所有好處,特別是對於轉換 XML 文檔的任務。
高級語言帶來的實際好處是開發生產力。但實際上,真正的價值源自於 更改的潛力 。與使用低級 DOM 和 SAX 接口編碼的過程性應用程序相比,用於轉換 XML 數據結構的 XSLT 應用程序更能適應對 XML 文檔細節的更改。在數據庫世界中,這種特性叫做 數據獨立性 ,正是由於數據獨立性導致了諸如 SQL 之類聲明性語言的成功,並使舊的引導性數據訪問語言走向衰亡。我堅信在 XML 世界中也會這樣。
當然與所有聲明性語言一樣,XSLT 也會降低性能。但是對於大多數應用程序,今天的 XSLT 處理器的性能已經完全能夠滿足應用程序的需要,並且它會變得越來越好。在我的第二篇文章中,我將討論 XSLT 處理器中使用的一些優化技巧,如我自己的 Saxon 產品。
|
我想要在本文中展示的是 XSLT 是一種用於操作 XML 文檔的完整高級語言,就如同 SQL 是操作關係表的高級語言一樣。應該注意到 XSLT 不僅是一種樣式設計語言,它比 CSS(或者甚至 CSS2)的功能更強大。
我見到過一些應用程序,它們的所有商務邏輯都用 XSLT 編碼。在一個三層在線銀行系統中,我看到:
- 從後端操作系統以 XML 消息的形式檢索所有數據。
- 在聯機會話的持續時間內,用戶的帳戶數據在內存中以 XML DOM 形式表示。
- 所有給用戶的信息首先封裝成 XML 消息,然後用服務器或客戶機附帶的 XSLT 轉換根據瀏覽器的性能將這些消息轉換成 HTML。
該應用程序的數據都是 XML 格式的,並且邏輯(包括數據訪問邏輯、商務邏輯和顯示邏輯)都由 XSLT 來實現。我建議每個項目都採用那種體系結構,但這還需要很長時間,我認爲我們會在幾年之內見到那種系統。
作爲一種編程語言,XSLT 有許多特性 -- 從它使用 XML 語法到其功能性編程原理的基礎 -- 還不爲一般 Web 程序員所熟悉。那意味着一條陡峭的學習曲線和通常遇到許多挫折。當初對於 SQL 也是如此,所有這些表示 XSLT 與以前的編程語言有着本質的區別。但不要放棄:它是功能非常強大的技術,值得努力學習。