級別: 初級
Benoit Marchal, 軟件工程師, Pineapplesoft
2001 年 1 月 01 日
無
論是 XSLT 的初學者還是經驗豐富的程序員,都一定會發現 Benoit Marchal
的這五種技巧將改進編碼并帶來新的思路。這些技巧涵蓋了利用 XSL 樣式表(包括 HTML 實體)使用 CSS、合并客戶機端
JavaScript、使用多個輸入文檔,以及使用 XSLT 自動生成樣式表。本文包括了供您改編和再使用的樣本代碼。
XML 和 XSLT 的組合已在大中型網站的網站管理員之間逐漸流行起來。在
XSLT
出現之前,更改網站顯示是一項重要任務:有一個人必須重新訪問并更改站點上的每一個頁面。但
XSLT 可以自動執行這個過程,這就大大節省了時間。
技巧 1:級聯樣式表、表和
XSLT
在討論 XSLT 的文章中以關于 CSS
的技巧作為開始似乎有些奇怪,但有人經常問我“這兩種樣式表語言是否兼容?”答案是響亮的“是”。
清單 1 中的 products.xml 說明了這一點,它是以 XML
格式編寫的產品列表。請花一點時間來熟悉
products.xml,因為我將使用它來說明這五種技巧。
清單 1. products.xml,XML
格式的產品清單
<?xml version="1.0"?> <products> <product > <name>Playfield Text</name> <price currency="usd">299</price> <description>Faster than the competition.</description> <version>1.0</version> </product> <product > <name>Playfield Virus</name> <price currency="eur">199</price> <description>Protect yourself against malicious code.</description> <version>5.0</version> </product> <product > <name>Playfield Calc</name> <price currency="usd">299</price> <description>Clear picture on your data.</description> <version>1.5</version> </product> <product > <name>Playfield DB</name> <price currency="cad">599</price> <description>Organize your data.</description> </product> </products>
|
要將此文檔的格式轉換成 HTML,可以使用清單 2 中的
table.xsl。將 table.xsl 應用到
products.xml的結果如
圖 1
所示。可以看到,每隔一行都有一個灰色背景,這就提高了可讀性。在
table.xsl 中,這是由于使用了級聯樣式表而實現的。
清單 2. table.xsl,使用 CSS
的 XSLT 樣式表
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no"/>
<xsl:template match="/products"> <html> <head> <title>Cascading Style Sheet</title> <link rel="stylesheet" type="text/css" href="table.css" title="Style"/> </head> <body> <table> <tr class="header"> <td>Name</td> <td>Price</td> <td>Description</td> </tr> <xsl:apply-templates/> </table> </body> </html> </xsl:template>
<xsl:template match="product[position() mod 2 = 1]"> <tr class="odd"> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/></td> <td><xsl:value-of select="description"/></td> </tr> </xsl:template>
<xsl:template match="product"> <tr class="even"> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/></td> <td><xsl:value-of select="description"/></td> </tr> </xsl:template>
</xsl:stylesheet>
|
|
小技巧:更小的 HTML 文件
請注意
清單 2 中
table.xsl 的
<xsl:output> 元素中以紅色突出顯示的
indent="no" 屬性。這個屬性告訴 XSLT 處理器不要縮排
HTML 文檔,這通常會生成一個較小的 HTML 文件,可以使下載更快。
|
|
圖 1. HTML
格式的產品列表
Name |
Price |
Description |
Playfield Text |
299 |
Faster than the competition. |
Playfield Virus |
199 |
Protect against malicious code. |
Playfield Calc |
299 |
Clear picture on your data. |
Playfield DB |
599 |
Organize your data. |
它是如何工作的?
table.xsl 樣式表將 HTML
<link>
元素插入到輸出中。
<link>
會裝入級聯樣式表:
<link rel="stylesheet" type="text/css" href="table.css" title="Style"/>
|
CSS 和 XSLT 之間沒有沖突,因為不會同時使用它們。首先將 XSLT
樣式表應用到 XML 文檔
products.xml 。這會生成傳遞給瀏覽器的 XML
文檔。實際上,HTML 文檔裝入級聯樣式表與此階段無關。僅當瀏覽器裝入
HTML 文檔時,才會使用 CSS。
為了使用 CSS,XSLT 樣式表
table.xsl
會在適當的位置再插入
class
屬性。實際上,表中的元素被標記成
odd
或
even
類。那如何識別 XSLT
中的奇數行呢?只要將一個條件添加到
match
屬性,如從清單 2 中的樣式表中摘錄的這些行中所示:
<xsl:template match="product[position() mod 2 = 1]"> <tr class="odd"> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/></td> <td><xsl:value-of select="description"/></td> </tr> </xsl:template>
|
清單 3 中的簡單級聯樣式表
table.css
在表的偶數行上放置灰色背景。
清單 3. table.css,圖 1
中表的 CSS
.header { background-color: #999999; font-weight: bold; } .odd { background-color: normal; } .even { background-color: #dfdfdf; }
|
什么時候應該混合 CSS 和 XSLT?我認為此組合適用于以下情況:
- 要獲取更多的顯示控制,因為 CSS 的功能比原始 HTML 更強大
- 要生成更小的 HTML 文件,以便可以更快地下載
但是請注意,當組合這兩種樣式表時,在可維護性方面就會有所損失。事實上,設計人員最初是因為用一個文件就能控制整個網站的設計才會鐘情于
CSS。在那方面,XSLT 與 CSS 一起使用簡直就是多余,XSLT
就已經可以使一次性重新格式化幾個文檔變得很容易。
小心!
table.xsl
和本文中的其它樣式表需要使用符合標準的 XSLT 處理器。Internet
Explorer 5.0 和 5.5 附帶的處理器并不符合標準。如果需要一個合適的
XSLT 處理器,可以嘗試使用 Xalan,它是 Apache 項目中的 XSLT
處理器,或者將 IE 中的處理器升級到版本 3.0(請參閱
參考資料)。
技巧 2:HTML 實體
開發人員常問的另一個有關編寫 XSLT 樣式表的問題是如何插入 HTML
實體。尤其是,如何插入
實體(不可中斷空格)。在其它樣式表中,
空格用于在表中創建非空單元。
糟糕的是,這種直觀的解決方案不起作用:
為什么?XSLT 樣式表是一個 XML
文檔。對它進行分析時,會解析實體,而且會將
實體當作 XML 實體來進行解析。由于沒有在 HTML 中定義
,因此它會導致一個錯誤。
在清單 4 的
nbsp.xsl
中說明了變通方法,并且以紅色突出顯示了有關元素。可以看到,每次編寫
HTML
格式的實體時,它使用的字符比您預期輸入的字符要多。但如果您知道了代碼之后,只要在適當位置剪貼這段代碼即可。
清單 4. nbsp.xsl,演示如何在樣式表中插入 HTML 實體
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="no"/>
<xsl:template match="/"> <html> <head><title>HTML Entities</title></head> <body> <table border="1"> <tr> <td>Name</td> <td>Price</td> <td>Description</td> <td>Version</td> </tr> <xsl:apply-templates/> </table> </body> </html> </xsl:template>
<xsl:template match="product[version]"> <tr> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/></td> <td><xsl:value-of select="description"/></td> <td><xsl:value-of select="version"/></td> </tr> </xsl:template>
<xsl:template match="product"> <tr> <td><xsl:value-of select="name"/></td> <td><xsl:value-of select="price"/></td> <td><xsl:value-of select="description"/></td> <td> <xsl:text disable-output-escaping="yes"> </xsl:text></td> </tr> </xsl:template>
</xsl:stylesheet>
|
如清單 4 中所摘錄的,不可中斷空格實體的正確代碼是:
<xsl:text disable-output-escaping="yes"> </xsl:text>
|
那行代碼中發生了什么?訣竅是使用
<xsl:text>
元素以及
disable-output-escaping="yes"
屬性。
<xsl:text>
在輸出 HTML
中創建了一些文本。屬性告訴處理器不要將文本內容轉義。文本本身只是
和要轉義成 XML 格式的
&
字符。
再次聲明,樣式表是當作 XML 文檔讀取的,因此
就是
。如果告訴處理器不要轉義輸出 HTML 中的
&
,那么它會寫下
。
|
小技巧:多個輸出文檔
相反情況會怎么樣,即需要多個輸出文檔?根據一個樣式表創建幾個輸出文檔會如何?不幸的是,在撰寫本文時,還沒有標準解決方案。大多數
XSLT 處理器都有一些多個輸出文件支持,如 Xalan 中的
org.apache.xalan.xslt.extensions.Redirect 。
不要對標準解決方案絕望,XSLT
1.1(目前仍在開發中)將支持多個輸出文檔。
|
|
技巧 3:多個輸入文檔
典型的 XSLT 樣式表將一個 HTML 文檔轉換成另一個 XML 文檔,或者轉換成
HTML
文檔。有時,這太過于局限性。(有關如何處理相反情況的簡要介紹,請參閱側欄
多個輸出文檔。)
例如,請注意在
products.xml
中,
<price>
元素有一個
currency
屬性。貨幣不是用普通文字表示,而是使用代碼(例如,
usd
代表美元,或者
cad
代表加元)。您可能想要在顯示代碼之前先轉換代碼。
由于全世界有一百多種貨幣,而許多應用程序會處理這些貨幣,的確應該在一個獨立的
XML 文檔中存儲貨幣符號列表。清單 5 中的
codes.xml摘錄了這樣的文件。
清單 5.
codes.xml,帶貨幣符號代碼的 XML 文檔
<?xml version="1.0"?> <currencies> <currency> <code>eur</code> <name>Euros</name> </currency> <currency> <code>usd</code> <name>Dollars</name> </currency> <currency> <code>cad</code> <name>Canadian dollars</name> </currency> </currencies>
|
實際上,現在示例有兩個 XML 文件,
products.xml和
codes.xml ,您需要組合這兩個文件來創建 HTML
文檔。幸好,XSLT 使組合幾個輸入文件變得更容易,如清單 6 中
multi.xsl所說明的。
multi.xsl 中有兩個重要步驟。首先,樣式表打開
codes.xml (使用
document()
函數),然后將它賦值給
currencies
變量:
<xsl:variable name="currencies" select="document('codes.xml')/currencies"/>
|
然后,樣式表可以通過變量從代碼清單中抽取信息。當然,可以使用
XPath 來查詢貨幣文檔:
<xsl:value-of select="$currencies/currency[code=$currency]/name"/>
|
清單 6. multi.xsl,組合幾個 XML 文檔的樣式表
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no"/>
<xsl:variable name="currencies" select="document('codes.xml')/currencies"/>
<xsl:template match="/"> <html> <head><title>Multiple documents</title></head> <body> <table> <tr bgcolor="#999999"> <td>Name</td> <td>Price</td> <td>Description</td> <td>Version</td> </tr> <xsl:apply-templates/> </table> </body> </html> </xsl:template>
<xsl:template match="product"> <xsl:variable name="currency" select="price/@currency"/> <tr> <td><xsl:value-of select="name"/></td> <td> <xsl:value-of select="price"/> <xsl:text> </xsl:text> <xsl:value-of select="$currencies/currency[code=$currency]/name"/> </td> <td><xsl:value-of select="description"/></td> <td><xsl:value-of select="version"/></td> </tr> </xsl:template>
</xsl:stylesheet>
|
技巧 4:XSLT 和客戶機端
JavaScript
雖然 XSLT
功能強大,但它還不能應付一些情況。例如,您也許想要使用客戶機端腳本,如
JavaScript、JScript 或 VBScript。
如同 CSS,XSLT 并沒有對所生成的 HTML 有所限制 --
它還可以使用腳本。而且,正如清單 7 中
javascript.xsl 所說明的,可以將值從 XSLT 傳遞到
JavaScript。
清單 7. javascript.xsl,生成客戶機端 JavaScript 的樣式表
再次聲明,要理解
javascript.xsl ,需要記住在哪個位置發生了什么事。首先應用
XSLT,生成 HTML 文件。文件包括了由 XSLT 生成其內容的
<script>
元素。接著,瀏覽器裝入 HTML
文件,并執行腳本。請記住,腳本是由瀏覽器執行的,而不是樣式表。
例如,在
javascript.xsl
中以紅色突出顯示的行中,樣式表初始化 JavaScript
數組;實際上,是將值傳遞給腳本。
稍后,樣式表在函數中生成調用,并再次將值傳遞給腳本(通過 XSLT
position()
函數),如
清單 7
中以藍色突出顯示的行所示。
清單 8 顯示了在 HTML 中生成的內容。當用戶單擊
<a>
標記時,瀏覽器將執行這個腳本,如下所示:
清單 8. 根據 javascript.xsl 生成的
HTML
<ul> <li><a href="javascript:doSelect(1)">Playfield Text</a></li> <li><a href="javascript:doSelect(2)">Playfield Virus</a></li> <li><a href="javascript:doSelect(3)">Playfield Calc</a></li> <li><a href="javascript:doSelect(4)">Playfield DB</a></li> </ul>
|
能否從 XSLT 樣式表中,而非 HTML 文檔中調用 JavaScript
腳本?當然可以,但要使用 XSLT 擴展名。糟糕的是,XSLT 1.0
中并沒有完全標準化 XSLT 擴展名。XSLT 1.1 將改進這個支持。
技巧 5:自動創建樣式表
這種技巧是所有這五種技巧中最具挑戰性的。
有些情況下,您必須編寫許多樣式表,但似乎可以使用一個樣式表來創建它們。其實,這并不如您認為的那樣困難,它特別適用于用不同語言編寫的網站,或者那些擁有大量網頁、但僅是具體內容有所區別的站點。
清單 9 中的
start.xsl
樣式表說明了這一點。乍一看,它有點象典型的 XSLT
樣式表。如果仔細觀察,將會發現諸如
<link>
和
<para>
之類的特殊元素并不是 HTML 元素(HTML
元素應該是
<a>
或
<p>
)。而且,
<xsl:output>
元素沒有
method
屬性。
清單 9. start.xsl,普通 XSLT 樣式表
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output/>
<xsl:template match="/"> <page title="XSLT Through Generation"> <xsl:for-each select="products/product"> <para> <link href="{@href}"><xsl:value-of select="name"/></link> </para> </xsl:for-each> </page> </xsl:template>
</xsl:stylesheet>
|
訣竅是使用清單 10 中的另一個樣式表
generate_html.xsl將
start.xsl轉換成更典型的樣式表。
清單 10. generate_html.xsl,用于將 start.xsl 改寫成 HTML
的樣式表
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="xsl:output"> <xsl:copy> <xsl:attribute name="method">html</xsl:attribute> </xsl:copy> </xsl:template>
<xsl:template match="page"> <html> <head><title><xsl:value-of select="@title"/></title></head> <body> <h1><xsl:value-of select="@title"/></h1> <xsl:apply-templates/> </body> </html> </xsl:template>
<xsl:template match="para"> <p><xsl:apply-templates/></p> </xsl:template>
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template>
<xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
</xsl:stylesheet>
|
generate_html.xsl樣式表將
start.xsl 當作 XML
文檔處理,并將它轉換成另一個 XML 文檔。實際上,start.xsl
本身就是一個 XSLT 樣式表,并且對于 generate_html.xsl
沒有意義。例如,以下規則將
<link>
元素轉換成
<a>
:
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template>
|
這段代碼通過添加
method
屬性來修正
<xsl:output>
元素:
<xsl:template match="xsl:output"> <xsl:copy> <xsl:attribute name="method">html</xsl:attribute> </xsl:copy> </xsl:template>
|
不經修改復制樣式表中的其它元素(包括
<xsl:template>
、
<xsl:for-each>
和其它 XSLT 指令):
<xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
|
將 generate_html.xsl 應用到 start.xsl 會生成一個樣式表
generated.xsl 。創建 HTML 文檔的是
generated.xsl,而不是
start.xsl。
這個技巧并不是帶著您繞圈子,它是個非常好的示例,體現了使 XSLT
的邏輯達到最大化帶來的好處。實際上,由于 XSLT 非常適用于將 XML
文檔轉換成其它 XML 文檔,當然它也適用于轉換 XSLT 樣式表本身。
清單 11.
generated.xsl,演示如何將 start.xsl 轉換成 HTML
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/"> <html><head><title>XSLT Through Generation</title></head> <body><h1>XSLT Through Generation</h1> <xsl:for-each select="products/product"> <p> <a href="{@href}"><xsl:value-of select="name"/></a> </p> </xsl:for-each> </body></html> </xsl:template>
</xsl:stylesheet>
|
當網站需要創建許多樣式表時,您可能想要應用這個技巧。例如,通過將
generate_html.xsl替換成
generate_wml.xsl,可以自動將
start.xsl 改編成 WML 格式(WML
是無線智能電話的標記語言)。
清單 12.
generate_wml.xsl,它將 start.xsl 改編成 WML
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="xsl:output"> <xsl:copy> <xsl:attribute name="method">xml</xsl:attribute> <xsl:attribute name="doctype-public">-//WAPFORUM//DTD WML 1.1//EN</xsl:attribute> <xsl:attribute name="doctype-system">http://www.wapforum.org/DTD/wml_1.1.xml </xsl:attribute> </xsl:copy> </xsl:template>
<xsl:template match="page"> <wml> <card title="{@title}"> <xsl:apply-templates/> </card> </wml> </xsl:template>
<xsl:template match="para"> <p><xsl:apply-templates/></p> </xsl:template>
<xsl:template match="link"> <anchor><go href="{@href}"/><xsl:apply-templates/></anchor> </xsl:template>
<xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
</xsl:stylesheet>
|
本文寓意:如果尋求大型網站的最大自動化,根據 XML 文檔自動生成 HTML 文檔是不夠的,還應該自動生成 XSLT 樣式表。
參考資料
關于作者