XML 的使用越來越廣泛,但是很多 XML 的結構並不好。即便結構良好,也常常設計得很糟,使得處理和維護非常困難。而大部分用於 XML 的基礎結構使問題更加惡化。於是出現了關於 XML 最佳實踐的公開討論,比如 Henri Sivonen 的文章“HOWTO Avoid Being Called a Bozo When Producing XML”。Uche Ogbuji 經常在 IBM developerWorks 上討論 XML 最佳實踐,這裡他提出了在這些文章中討論的要點。
幾年來我一直在本專欄和其他系列文章中討論 XML 最佳實踐。其他人,比如和我同行的專欄作家 Elliotte Rusty Harold 也談到這個問題。參加 XML 設計原則討論的 XML 專家越好越好,這樣社區就會對在不同層次上采用 XML 的開發人員提供一致的建議。本文將結合最新和過去的文章,更詳細地介紹了 XML 最佳實踐。
不再有笨蛋
Henri Sivonen 撰寫了一篇有用的文章“HOWTO Avoid Being Called a Bozo When Producing XML”(請參閱參考資料)。他采用了基於 XML 的 Web 提要格式(如 RSS 和 Atom)的觀點,提出了用名稱空間生成結構良好的 XML 應該做和不應該做哪些事情的指南。正如他在簡介中所說的:
有些開發人員似乎認為以編程方式生成 XML 而保持結構良好非常困難(如果不是不可能的話),另一些開發人員卻能夠做到這一點,並奇怪其他人為什麼如此無能。我假設沒有人願意顯得無能或者被點名。因此,我希望下列建議能夠幫助開發人員從第一類人轉變成第二類人。
Henri 給出的第一條建議是“不要將 XML 看作是文本格式”,我認為這是一條危險的建議。當然其基本觀點是正確的 —— 不能像簡單的文本文檔那樣隨心所欲的生成和編輯 XML,但是這種要求適用於所有有結構的文本格式。但是,說 XML 不是文本就背棄了 XML 一個最重要的特點,而這點在規范的 XML 定義中被奉為圭臬。(“文本對象是結構良好的 XML 文檔[如果符合本規范]”。) Henri 的提法也讓人糊塗,因為有關於 XML 文本的技術定義,大致上是將它解釋為 XML 的字符序列。文本不僅僅是葉子元素或者屬性中的主要成分 —— 技術上稱這類文本為字符數據。文本還是所有 XML 實體的主要成分,因此說 XML 不是文本是自相矛盾的。我認為強調 XML 和開發人員已經熟悉的文本格式的區別會更有意義。
上面的評述表明,Henri 的建議可能由於過分關心生成結構良好的 Web 提要的問題而有些偏激。警告人們簡單的堆砌字符串,期望它成為結構良好的 XML 的做法是危險的,這一點上他是正確的。我也在文章中建議人們使用成熟的 XML 工具箱而不是使用簡單的文本工具來創建 XML(請參閱參考資料)。我疑慮的是 Henri 描述這個建議的方式有點混亂,在更廣泛的 XML 處理上下文中會造成誤解。他在“Don't use text-based templates”和“Don't print”兩節中反復重申這個觀點。我認為可以將他的建議歸納為“不要使用不能保證產生結構良好的 XML 的機制。”這確實是一項很重要的建議。正像 Herni 所提到的,安全創建 XML 的一種方法是發送 SAX 事件,“使用樹或棧(或者 XML 解析器)”。但即使這樣做也不能令您高枕無憂。使用的 SAX 工具不一定要進行所有必要的結構良好性檢查。比如,XML 中禁止某些 Unicode 字符。為了解決這些問題可能需要進行額外的檢查。
Henri 建議用戶不要嘗試手工管理名稱空間,這是正確的。我曾經在 developerWorks 上討論過,必須非常小心地處理 XML 名稱空間。他建議開發人員,按照統一名[名稱空間統一資源標識符(URI)加上本地名]來考慮一般情況就行了,但有時候不可避免地要面對前綴或者 XML 聲明。在 XSLT 這樣的規范中,QName(前綴/本地名組合)可在屬性值中使用,並假定前綴根據作用范圍內的名稱空間聲明解釋。這種模式稱為上下文中的 QName。在這種情況下,開發人員必須控制聲明的前綴,否則 XML 處理就會失敗。如果開發人員管理自己的名稱空間聲明,由於 XML 名稱空間的復雜性,結果往往會顯得雜亂無章。
因為經過 XML 處理管道之後名稱空間語法可能變得非常混亂,一種解決方法是在管道的最後插入一個規范化步驟。XML 規范化消除了 XML 1.0 和 XML 名稱空間允許的各種語法變體,包括不同的名稱空間聲明方式。規范化不能消除使名稱空間聲明對開發人員變得危險的所有問題。規范化也不能解決上下文中的 QName 問題,因為它並沒有改變文檔中使用的前綴,但它確實可以減輕名稱空間聲明的混亂程度,使您很容易確定問題所在,甚至可以編寫代碼自動糾正這些問題。GenX 庫是 Henri 建議使用的 XML 創建工具之一,能夠自動生成規范的 XML,其他很多工具箱也作為選項提供了規范化功能。
Henri 關於 Unicode 和字符處理的建議基本上是完全正確的。不過我認為“Avoid adding pretty-printing white space in character data”一節有點誇大其詞。多數情況下,元素之間而不是帶有字符數據的元素內部的精細打印是安全的。如 Henri 所述,清單 1 所示的如果以清單 2 的形式呈現通常是不安全的。
清單 1. XML 例子
<foo>bar</foo>
清單 2. 在字符數據中增加空白後的 XML 例子
<foo>
bar
</foo>
但通常以清單 3 的形式打印 XML 是安全的,輸出結果如清單 4 所示。
清單 3. 另一個 XML 例子
<doc><foo>bar</foo></doc>
清單 4. 清單 3 中的 XML 在字符數據中增加了空格
<doc>
<foo>bar</foo>
</doc>
很多 XML 序列化工具能夠理解相對安全和不安全的的打印格式。必須知道的是,如果在混合內容中增加空格,則清單 3 和 4 中所示的精細打印形式可能造成扭曲。如果使用模式制導的序列化,則可以避免這類問題。但在實踐中,使用混合內容的多數詞匯表對空白規范化沒有這麼敏感,因此不用過於擔心精細打印。應該充分了解該問題,並知道沒有辦法關閉精細打印(最好默認不用精細打印)。Henri 提出了清單 5 所示的精細打印實踐,但是我不同意,因為我認為那些難看的標記不容易理解。
清單 5. Henri Sivonen 建議但本文作者不同意的精細打印方式
<foo
>bar</foo
>
修道院的建議
現在換換檔,本文要探討的第二篇資料是 Simon St. Laurent 撰寫的“Monastic XML”(請參閱參考資料)。這是一組小短文,圍繞著如何充分利用 XML 而就處理和思考 XML 提出了一些建議。Simon 使用修道院和禁欲主義作為比喻,提出為 XML 增加不適應其簡單文本根 (textual root) 的過多負擔是危險的。 在“Marking-up at the foundation”中,他討論了字符數據和標記(元素和屬性)的本質作用。在“Naming things and reading names”中,他解釋了為何一般標識符(也稱為元素類型名)是一個重要的概念,應該作為標記信息結構的惟一關鍵成分。理想情況下,如果使用 XML 名稱空間,關鍵就是統一名稱(名稱空間 URI 加上本地名),這種復雜化就是 Simon 在“Namespaces as opportunity”中厲聲疾呼的原因之一。“Accepting the discipline of trees”揭示了 XML 一個不幸的秘密:盡管看起來 XML 的層次結構很容易擴展成圖形結構,但實踐證明用 XML 建模圖有點困難。但目前為止,“Monastic XML”網站上最重要的建議是“優化標記的處理總是不成熟”。XML 是一種聲明性技術,對很多開發人員來說,關於它的強大和不足有很多不實之詞。那些盡量把 XML 設計和處理細節拉近的開發人員,從長期來看,通常使得處理更加困難。XML 成功的關鍵是關注需要抽象表示的信息的特點,將它與需要處理這些信息的系統的技術設計分離開來。
結束語
討論 XML 最佳實踐時總是有一些不同的觀點,特別是在初期階段,但聽到不同的聲音是一件好事。關於這個話題的參考資料很少,我將繼續在本專欄中討論它。如果對最佳實踐有什麼資料或者建議或者希望分享您的觀點,請參加 Thinking XML 論壇上的討論。