歡迎來到 XML轉換的世界!我想您可能不會一帆風順:標准正在接合並進行修訂、工具還不成熟並經常有錯誤、實現不一致,並且選擇非常混亂。但不要驚慌。我可以為您指引至少一條走出迷宮的途徑。而且隨著時間的推移,情況必定會有所改善,盡管改善的速度往往不如我們所願。
首要事情
最後兩個“XML 問題”專欄描述了我將學術文章轉換成 XML的項目,具體來說是轉換到 DocBook DTD的項目。那些文章提供了一個編寫您自己的 DocBook文檔的良好起點,這就是本專欄目的所在。
在本專欄中,我們假設您已經有了一些結構良好、格式正確、有效的DocBook XML文檔。首先非常好的事是擁有它們,但下一步是將它們轉換成更加方便的最終用戶格式:例如Html 頁面、PDF文件和印刷頁面(讀者實際所閱讀的)等。這正是我在將部分歸檔作品轉換成DocBook 後所面臨的問題,本文提供了我自己的解決方案。
我的主要目標 -- 至少目前 -- 是到 HTML 的正確轉換。但我不希望限於HTML輸出。還有一些更小的目標。我希望對精確輸出具有某些控制而無需執行許多操作,也無需了解許多新的語言和技術。我還希望使用免費的工具和跨平台的工具。最後,我希望將依賴性降低到最小。即使所有必需的貢獻都是免費和跨平台的,大量復雜的依賴性也是個缺點。基本上,我的理想是有一個獨立的可執行程序,只運行,可靠地運行,將我的DocBook 文檔以我所希望的樣式轉換成Html。高不可攀的夢想,但為什麼不能這麼想呢?
執行轉換的方式
至少有四種可能的方式來將 DocBook 文檔 -- 或幾乎所有 XML 文檔 --轉換成最終用戶格式。我很認真地為我的小項目考慮過所有四種方式。本專欄只詳細討論最後一種選項,但在您規劃涉及到重復轉換的項目時,有必要記住所有方式:
編寫定制轉換代碼。最好從一種具有基本 XML方法庫的編程語言(例如 SAX 和DOM)開始。但即使假設基本語法分析是個黑箱,定制代碼也可以對語法分析過的元素執行所有您希望執行的操作。歸根結底,這是最靈活和最強大的方式,但也往往會帶來更多工作,不論是事先的還是維護的。
使用級聯樣式表和 DocBook文檔。這是一種想法。最好能將排版規范完全與結構化標記分離,並只讓客戶機設備(例如浏覽器)產生良好的輸出。這有可能發生,但就目前來說,似乎支持有限-- 只在 IE 5.5、Opera 4 和某些最新的 Mozilla開發者發行版中支持。現在這不象是可以依靠最終用戶為他們執行這些任務的方法。
使用文檔樣式語義和規范語言 (DSSSL)來指定到目標格式的轉換。從好的方面說,已經存在一些 DocBook(以及其它格式)DSSSL 樣式表。DSSSL基本上是一門需要學習的全新編程語言,而且是類似於 Lisp的功能性語言。為利用 DSSSL,需要從 Jade 或 OpenJade開始,但這兩種工具都很復雜,以至於許多人都必須為它們編寫封裝器(例如SGML-tools Lite)。為了獲得有用的系統 --雖然有報告說是個非常好的有效系統 --您確實需要滿足所有種類的系統依賴性,並安裝所有種類的工具和庫。對於某些有良好意願,然而可能沒有投入足夠精力的嘗試,我沒能讓與Jade相關的工具在我的系統上順利地發揮作用。很明顯,有其他許多人每天都在使用這些系統,所以稍微多做一些工作一定能把事情辦得井井有條。(如果您能指點一個快速、簡單、多合一的DSSSL處理器,請告訴我。我非常希望嘗試一下。)然而除了安裝困難外,DSSSL感覺上象是來自與 XML技術不同的傳統和思考方法,相反,最後一個方法基本上是純XML,並來自正式(有效)的 W3C 規范。
使用可擴展樣式表語言轉換 (XSLT)。從某種意義上說,XSLT實際上是 XML 文檔類的一個規范。即,XSLT 樣式表本身是格式正確的 XML文檔,並帶有一些專門的內容,可以讓您“模板化”您所期望的輸出格式(繼續閱讀它的含義)。有許多工具至少從名義上支持XSLT:我的預感是,這確實是 XML 轉換的技術方向 --因為,或者盡管,相對於 W3C 的“正式”身份。XSLT可以指定到任何目標格式的轉換。但給我的一般感覺是,大多數開發者發現,在目標格式是另一種XSLT 格式(例如 XHtml)時,使用 XML 更容易。
選擇 XSLT 工具
參考資料部分包含了到許多 XSLT工具描述的鏈接。我嘗試過其中的許多,但發現 Sablotron最合我口味。它是自由軟件(GNU)。它是多平台的。它有許多獨立的可執行程序,這些程序可以簡單地從命令行運行。最重要的是,它看起來能正確工作,至少對於我的簡單測試案例來說如此。
XSLT.com 列出的許多其它 XSLT工具也是自由軟件。不過,它們中的大多數是 Java程序,也要依賴於各種額外的 Java 庫。用戶似乎給了一些 Java工具正面評價,因此這些工具對您來說可能是很好的選擇。我選擇Sablotron 是因為編譯過的 C 速度更快,安裝和使用都很簡單。
Norman Walsh 為 DocBook 創建了一系列 完整的XSLT樣式表。不幸的是,將它們用在樣式表時,Sablotron 崩潰了,並且 XMLSpy 不能匹配有效 DocBook 文檔中的任何東西。這很可能是工具而不是Walsh樣式表的一個限制。使用其它工具可能運氣會好些。這個問題仍然給了我們開發定制(不很完整)XSLT樣式表的機會,無論如何這是我真正希望的(為了演示技術)。
Sablotron 的使用非常簡單。基本用法是:
清單 1: Sablotron 的基本用法
X:mydocs> x:sablinsabcmd mystyle.xsl mydoc.XML
mydoc.Html
它說的是:使用 mystyle.xsl 中的規則來將 mydoc.XML 轉換成 mydoc.Html ,如果希望,也可以使用管道和重定向。設置Sablotron和解開它的檔案一樣容易(它還提供了能從程序調用的庫,但最好從命令行實用程序中使用。)因此可以按環境需要調整路徑和文件名。
編寫 XSLT 規范
有關 XSLT 的實質,請閱讀 W3C 的官方建議書(請參閱 參考資料部分)。本文旨在提供讓它工作的更多非正式細節。
“ XML 問題 #3”和“ XML 問題 #4”中介紹的特定 DocBook 文檔 ( chap5.xml )是一 章。示例使用了章節中所有可能的 DocBook標記中相當小的一部分。所以目前來說,我們實際上所需要的就是 chapter.xsl 文件,它將對 chap5.XML 中實際使用的每個標記做些有用的事。這是適當的開始,但易於構建是因為XSLT 具有開放和可擴展的本質。讓我們看一下。
從 chapter.xsl 的骨架開始 -- “如何將 DocBook章節轉換成 Html”模板:
清單 2: 骨架 XSLT 文檔 (empty.xls)
<xsl:stylesheet version="1.0"
XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns="http://www.w3.org/TR/xHtml1/strict">
<xsl:output method="Html" indent="yes" encoding="UTF-8"/>
</xsl:stylesheet>
可以看到, chapter.xsl 是個格式正確的 XML文件。您還將注意到,模式 <xsl:*> 是 XSLT文檔中許多標記的名稱。實際上,所有屬於指令的標記都象這樣。在轉換到類似XML 格式(例如Html)的過程中,將看到各種其它標記。這些其它標記屬於目標格式,只在 <xsl:*> 元素中出現。
基本上,應該確切使用如上指出的名稱空間屬性( xmlns:xsl 和 xmlns )。可能還希望保留輸出行;盡管可以使用 XML 或 text 方法。
上面的 XSLT文件作為處理模板使用得非常好。但這可能不是您所需要的。可以假設因為缺少輸出規范而沒有任何輸出。這不完全正確:它仍然捕捉所有文本節點,並提供簡單ASCII版本的章節(使用上述樣式表)。如果 確實希望沒有一點輸出,需要有類似清單3 中的一個 XSLT 文檔:
清單 3: Null 輸出 XSLT 文檔 (null.xls)
<xsl:stylesheet version="1.0"
XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns="http://www.w3.org/TR/xHtml1/strict">
<xsl:output method="Html" indent="yes" encoding="UTF-8"/>
<xsl:template match="*">
</xsl:template>
</xsl:stylesheet>
null輸出器使我們的轉換更有用。真正的樣式表實際上只描述了一系列要嘗試匹配的模式,模板在每個提供輸出內容模板的 <xsl:template> 元素內部。如示例所示,"*"可以與任何模式匹配。我們的示例碰巧沒有在模板中 做任何事,但它仍然努力與源XML/DocBook 文檔中可能出現的任何元素匹配。
通過下降進行匹配
XSLT的威力主要在於它們擴展匹配功能的能力。一旦匹配了一個元素,XSLT就將匹配功能擴展到該元素的子元素。通過在 null輸出器上進行擴展,讓我們創建一個有一定意義的樣式表。允許下降到子元素的重要標記是 <xsl:apply-templates> 。一般來說,每個模板都在其主體中包括這個標記:
清單 4: 最小章節 XSLT 文檔 (minimal.xls)
<xsl:stylesheet version="1.0"
XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns="http://www.w3.org/TR/xHtml1/strict">
<xsl:output method="Html" indent="yes" encoding="UTF-8"/>
<xsl:template match="chapter">
----- Start of Chapter -----
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
##### Unmatched Element in Source #####
</xsl:template>
</xsl:stylesheet>
使用該樣式表和 DocBook 章節運行 XSLT處理器時,得到的結果類似於:
清單 5: 使用樣式表和 DocBook章節的 XSLT 處理器
----- Start of Chapter -----
##### Unmatched Element in Source #####
##### Unmatched Element in Source #####
##### Unmatched Element in Source #####
該輸出不那麼有用,但它讓我們看到樣式表做了些什麼。章節的根元素是 <chapter> 標記。樣式表匹配 <chapter> 標記,並打印 " - - - - - Start ofChapter - - - - - "。各種子代出現在 <chapter> 元素中。每一這樣的子代都稱為非章節內容,因此將匹配 "*" 模板。
為開發自己的 XSLT樣式表,提供類似上面非匹配元素的某些明顯標記,可以讓您很快看到需要開發哪些模板。清單6 顯示了帶有一些真實模板的版本:
清單 6: 有效的 Html 輸出器 XSLT文檔
<xsl:stylesheet version="1.0"
XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns="http://www.w3.org/TR/xHtml1/strict">
<xsl:output method="Html" indent="yes" encoding="UTF-8"/>
<xsl:template match="chapter">
<Html>
<head>
<title>
<xsl:value-of select="title"/>
</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</Html>
</xsl:template>
<xsl:template match="chapter/title">
<hr></hr>
<h1><xsl:apply-templates/></h1>
</xsl:template>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="*">
##### Unmatched Element in Source #####
</xsl:template>
</xsl:stylesheet>
該 HTML 輸出器顯示了 XSLT 樣式表的某些實際特性。 chapter模板匹配對希望產生的 HTML 文檔進行排版。模板匹配內部的 HTML標記沒有什麼特別;您放在那裡的所有文本都將出現在輸出中。在 HTML <title> 元素中,使用 <xsl:value-of> 指令將 <chapter> 內部必需的 title子元素插入到DocBook。在 Html <body> 元素中,將控制傳遞給其它模板(大概 DocBook 中極少的一部分)。
chapter後的下一個模板是 chapter/title。這意味著匹配 <title> 元素,但只有在它直接出現在 <chapter> 內部時。如果希望,可以只匹配 title,從而指定源文檔中每個 <title> 元素的輸出格式。但我希望將章節標題格式化成不同於 sect1的標題、 sect2的標題等。使用示例中的 para(但從不真正匹配,因為 para只能出現在還不匹配的標記內部)。為進行准確的衡量,模板仍然匹配"*",因此可以在檢查輸出時看到樣式表不完整。
重復的子代
通過下降匹配模板不是 XSLT唯一的技巧。還可以執行有條件的輸出、排序、取出源屬性,然後在子代上循環。就目前來說,只需要看一下清單7 中簡單的循環示例:
清單 7: 在子元素上循環的 XSLT模板
<xsl:template match="simplelist">
<ul>
<xsl:for-each select="member">
<li><xsl:apply-templates/></li>
</xsl:for-each>
</ul>
</xsl:template>
不用下降到 simplelist中的每個子元素,我們只假設子元素都是 <member> 元素。 <xsl:for-each> 的工作方式與嵌套模板非常相似,而且與編程語言循環構造也非常相似。 <xsl:for-each> 元素的內容將出現在匹配 select屬性的每個子元素的輸出中。在循環中,當前 <member> 元素的內容成為下降到我們在循環中找到的 <xsl:apply-templates/> 標記的活動節點。就是說,列表中的每樣事物在其內部都有進一步的標記,我們將這些元素的格式傳遞給它們相應的模板(對於文本節點,它們就是文字格式的輸出)。
未來
前面的資料只揭開了 XSLT的面紗。但它應該為您提供了使用樣式表和轉換的一些認識。 參考資料部分提供了進一步閱讀相關問題的許多來源。特別是通過閱讀本文檔案文件中更完整的XML 和 XSLT 示例能從中獲益。請別走開,本專欄將以各種方式再回來介紹XSLT。