XSL:轉換從哪裡開始?
前言
愛好XML的人最終會試着将XML轉換為HTML,或者轉換為其他類型的文檔,DOM/SAX顯然不是專門為轉換設計的,CSS對于轉換也是力有不逮,是以XML的愛好者們幾乎無一例外的要遭遇XSL,但是XSL似乎有非常多的用法,對于XML僅僅隻是表示格式化的資料而言,XSL顯得複雜且毫無頭緒。
例如《跟我學XSL》和《XSL基礎入門》這樣的教程會帶給你XSL的一些概念和例子,但是對于XSL的運作環境、平台特性和本質,似乎都語焉不詳,你最終學會的僅僅是在XMLSPY或者IE中打開你的XML看看它轉換後的效果罷了。一有人提到腳本語言或者JAVA中調用XSL你就頭大了,甚至你不清楚XSL和XSLT究竟有什麼差別。迷失在網絡中的人們喜歡不停的用google搜尋你想要的中文資料,但是其實有那個時間,幹脆去那種技術的官方網站上好好看看吧。http://www.w3.org/Style/XSL/是XSL技術的W3C的官方網站,在網頁正文的第一行它就解釋和XSL和XSLT的差別。原文如下:
XSL is a family of recommendations for defining XML document transformation and presentation. It consists of three parts:
XSL Transformations (XSLT)
a language for transforming XML
the XML Path Language (XPath)
an expression language used by XSLT to access or refer to parts of an XML document. (XPath is also used by the XML Linking specification)
XSL Formatting Objects (XSL-FO)
an XML vocabulary for specifying formatting semantics
XSL是一組定義XML文檔的轉換和顯示特征的推薦标準,它包括三個部分:XSL轉換(XSLT)是一種為了轉換XML而定義的語言;XML路徑語言(XPath)是一種表達式語言,它被XSLT用來通路或者送出一個XML文檔的某些部分(XPath也同時被XML Linking标準使用);XSL格式化對象(XSL-FO)是一個XML詞彙表用來定義XML的格式化語義。
從何開始
一般人學習XSL都是從XMLSPY等工具開始運作他的一個XSL例子,當然用文本編輯器編輯XML何XSL檔案,用IE去打開XML也是一個好主意。因為XMLSPY和IE都有嵌入式的XSL解析器,例如IE的XSL解析器是MSXML,這樣不用顯式的調用XSL進行轉換過程,隻需要在XML文檔的頭部加上一句<?xml:stylesheet type="text/xsl" href="xxx.xsl" target="_blank" rel="external nofollow" ?>就可以讓嵌入的XSL解析器自動的進行轉換了。例如下面這個著名的例子,它包括cd_catalog.xml和cd_catalog.xsl檔案,内容如下:
xml檔案:
<?xml version="1.0" encoding="GB2312"?>
<?xml:stylesheet type="text/xsl" href="cd_catalog.xsl" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" ?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>喀什噶爾胡楊</TITLE>
<ARTIST>刀郎</ARTIST>
<COUNTRY>China</COUNTRY>
<COMPANY>先之唱片</COMPANY>
<PRICE>20.60</PRICE>
<YEAR>2004</YEAR>
</CD>
<CD>
<TITLE>敦煌(特别版)</TITLE>
<ARTIST>女子十二樂坊</ARTIST>
<COUNTRY>China</COUNTRY>
<COMPANY>百代唱片</COMPANY>
<PRICE>25.60</PRICE>
<YEAR>2005</YEAR>
</CD>
</CATALOG>
xsl檔案:
<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<html>
<body>
<table border="2" bgcolor="yellow">
<tr>
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="CATALOG/CD">
<tr>
<td>
<xsl:value-of select="TITLE"/>
</td>
<td>
<xsl:value-of select="ARTIST"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
将它們儲存在同一目錄下然後用IE5以上版本的IE直接打開xml檔案,則會看到轉換後的效果。當然用XMLSPY中自帶的浏覽器也可。
用JScript顯式調用XSL解析器
上面的運作方法顯然是“貪天之功”,利用了IE和XMLSPY自帶的XSL解析器,是讓一隻看不見的手運作了轉換過程。那麼,也可以用Jscript語言顯式的調用XSL解析器,讓沒有嵌入解析器的浏覽器也可以運作XSL,當然,此浏覽器必須支援Jscript腳本語言。我們還是使用上面的例子,不過将cd_catalog.xml中的<?xml:stylesheet type="text/xsl" href="cd_catalog.xsl" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" ?>這一行去掉,同時建立一個cd_catalog.html文檔,内容如下:
<html>
<body>
<script language="javascript">
// Load XML
var xml = new ActiveXObject("Microsoft.XMLDOM")
xml.async = false
xml.load("cd_catalog.xml")
// Load the XSL
var xsl = new ActiveXObject("Microsoft.XMLDOM")
xsl.async = false
xsl.load("cd_catalog.xsl")
// Transform
document.write(xml.transformNode(xsl))
</script>
</body>
</html>
将此html文檔在支援Jscript的浏覽器中打開,即可看到如前一段執行的結果。當然不僅僅是Jscript,其他的腳本語言如VBScript等等也可以,不過Jscript是XSL預設的腳本語言。
腳本擴充的XSL,令人疑惑的xsl:eval标記
xsl:eval标記并不是一個标準的xsl标記,它屬于http://www.w3.org/TR/WD-xsl這個名字空間,這個名字空間最終被微軟采用,于是xsl:eval也被微軟用來調用Jscript腳本,以此來擴充XSL的功能。而标準的XSL1.0版本的名字空間是http://www.w3.org/1999/XSL/Transform,它并不包含xsl:eval标記,這是很容易了解的,XSL應該屬于一個平台無關的技術,如果它的某個标記要依賴微軟公司的産品,那顯然是自掘墳墓。關于平台無關的讨論,将在本文的最後展開。
xsl:eval标記的含義是計算其中腳本語言的表達式,并作為文本輸出。下面的例子中計算了cd_catalog.xml中各種CD的總價格,修改上面的cd_catalog.xsl并另存為cd_catalog2.xsl檔案如下:
<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
<html>
<body>
<table border="2" bgcolor="yellow">
<tr>
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="CATALOG/CD">
<tr>
<td>
<xsl:value-of select="TITLE"/>
</td>
<td>
<xsl:value-of select="ARTIST"/>
</td>
</tr>
</xsl:for-each>
<tr>
<td>合計</td>
<td>
<xsl:eval>total("PRICE")</xsl:eval>
</td>
<xsl:script>
function total(q){
temp=0;
mark='/CATALOG/CD/'+q;
v=selectNodes(mark);
for(t=v.nextNode();t;t=v.nextNode()){
temp+=Number(t.text);
}
return temp;
}
</xsl:script>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
在IE中打開cd_catalog.xml檔案(注意修改xsl為cd_catalog2.xsl)即可看到結果,注意這個xsl檔案的這一行<xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/TR/WD-xsl">,寫錯了名字空間xsl:eval标記就會報錯。
浏覽器無關的XSL解決方案,服務端的XSL
不管如何折騰,要将XML通過XSL轉換為HTML必須要求本地主機上有一個XSL解析器,不管是浏覽器内嵌的,還是可以通過腳本語言調用。那麼,更好的解決方案當然是從伺服器端直接發送HTML回來,這樣無論什麼浏覽器都可以看到轉換的結果了。ASP提供了這個功能,這是可想而知的,不過我對ASP不熟,這段略過,有興趣的可以找本ASP的XML教材看看。
應用程式中的XSL,語言相關的XSL
衆所周知,Java是對XML技術支援得最好的語言,Java上面的xml包非常多,其中支援XSL轉換的包最著名的有Saxon和xalan。Saxon包可以在http://saxon.sourceforge.net/上面下載下傳。将Saxon包解壓縮到C:\saxon6_5_3,6.5.3版本提供了對XSL1.0最穩定的支援。然後在Classpath中加入C:\saxon6_5_3\saxon.jar;C:\saxon6_5_3\saxon-jdom.jar。
Saxon提供指令行式的XSL轉換和API。其中指令行式的轉換如下,将目錄移動到存放xml(去掉xml的指定xsl的那一行)和xsl的目錄,然後輸入下面的指令:
java com.icl.saxon.StyleSheet cd_catalog.xml cd_catalog.xsl
就可以看到輸出在螢幕上的結果,但是這樣看起來不友善,是以輸入如下指令:
java com.icl.saxon.StyleSheet cd_catalog.xml cd_catalog.xsl>a.html
然後将生成的a.html在浏覽器中打開,可以清晰的看到結果。
下面是在Java程式中調用Saxon包,進行XSL轉換的例子,檔案名為XslExam.java:
import java.io.File;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import com.icl.saxon.ExtendedInputSource;
import com.icl.saxon.TransformerFactoryImpl;
public class XSLExam {
public static void main(String[] args) {
String sourceFileName = "cd_catalog.xml";
String styleFileName = "cd_catalog.xsl";
String outputFileName = "result.html";
File sourceFile = null;
File styleFile = null;
File outputFile = null;
TransformerFactoryImpl factory = new TransformerFactoryImpl();
Source sourceInput = null;
sourceFile = new File(sourceFileName);
ExtendedInputSource eis = new ExtendedInputSource(sourceFile);
sourceInput = new SAXSource(factory.getSourceParser(), eis);
eis.setEstimatedLength((int)sourceFile.length());
Source styleSource ;
File sheetFile = new File(styleFileName);
eis = new ExtendedInputSource(sheetFile);
styleSource = new SAXSource(factory.getStyleParser(), eis);
outputFile=new File(outputFileName);
try {
Templates sheet = factory.newTemplates(styleSource);
Transformer instance = sheet.newTransformer();
Result result = new StreamResult(outputFile);
instance.transform(sourceInput, result);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}catch (TransformerException err) {
err.printStackTrace();
}
}
}
這個例子程式将cd_catalog.xml檔案使用cd_catalog.xsl轉換為result.html。在Eclipse3.01中調試通過(Saxon沒有簡單的xsl示例程式,我也是将com.icl.saxon.StyleSheet類拔光了才得到這個稍微簡單的例子,如果需要更詳細的用法,參考com.icl.saxon.StyleSheet類)。
資料是獨立的,處理是平台相關的
總結前面的内容,可以看出XSL轉換可以從這幾個地方開始:
Ø IE,XMLSPY:嵌入的解析器,例如MSXML3;
Ø JScript,顯式調用XSL解析器;
Ø 用JScript擴充XSL功能,半吊子的XSL;
Ø 浏覽器無關的XSL解決方案,伺服器端的XSL,ASP顯式調用XSL;
Ø 語言相關的XSL,Java的XSL包Saxon,xalan。
可以看出來,XSL無論如何,都是要平台相關的,第一種方法依賴嵌入浏覽器的XSL解析器;第二、三種方法依賴作業系統安裝的XSL解析器;第四種方法依賴伺服器端安裝的XSL解析器;最後的方法依賴JAVA語言提供的XSL API。其中微軟還不顧W3C的反對,自定義了XSL的腳本擴充功能,功能倒是強大了,可惜脫離了Windows就玩不轉了。JAVA号稱平台無關,可是JAVA本身就是一個平台,要是有人的機器沒有JRE又怎麼辦呢?丢棄XSL?
不過事物總是有因果的,其實XML作為資料的存儲載體,可以做到完全的平台無關,但是XSL作為一個可執行的語言,一定要依賴某種已存在的運作環境的,就如同資料庫中的表格和SQL語言一樣。SQL号稱适用于任何關系資料庫,但是實際上還是需要一個環境來run的。那麼XSL是否破壞了XML的平台無關性呢?我認為沒有,因為XSL本身是一個XML文檔,XML文檔可以平台無關的儲存和傳輸,至于使用何種方法來調用它則是另外考慮的問題。再者,XSL的源和目标都是平台無關的文檔(例如XML和HTML),而它自己的調用方式則是可替換的,這點也減輕了XSL的負罪感吧。
以上的讨論都是基于XSL1.0标準的,目前XSL2.0标準尚在讨論中,不過初稿已經釋出了,而Saxon8.0以上的版本号稱已經支援了XSL2.0。讓我們拭目以待XSL2.0帶給我們的驚喜。
參考文獻
W3C站點:http://www.w3.org/Style/XSL/
XSL主題:http://www-900.ibm.com/developerWorks/cn/xml/theme/x-xsl.shtml
中文譯文站點:http://www.opendl.com/
XSLT是什麼類型的語言,SAXON的作者談XSL:http://www-900.ibm.com/developerWorks/cn/xml/x-xslt/index.shtml