即使您不想使用 DOM 實現,但還是值得瀏覽下面對 DOM 用法的描述。因為 DOM 樣本是第一個樣本,所以與後面的模型相比,我用它來探究有關該樣本的一些問題和結構的更詳細資料。瀏覽這些內容可以補充您想知道的一些細節,如果直接閱讀其它模型之一,那麼將錯過這些細節。
DOM
DOM 規範涵蓋了文檔表示的所有類型的操作,但是它沒有涉及例如對文檔的文法分析和產生文本輸出這樣的問題。包括在效能測試中的兩種 DOM 實現,Xerces 和 Crimson,對這些操作使用不同的技術。清單 1 顯示了 Xerces 的頂級代碼的一種形式。
清單 1. Xerces DOM 頂級代碼
1 // parse the document from input stream ("in")
2 DOMParser parser = new DOMParser();
3 parser.setFeature("http://xml.org/sax/features/namespaces", true);
4 parser.parse(new InputSource(in));
5 Document doc = parser.getDocument();
6 // recursively walk and modify document
7 modifyElement(doc.getDocumentElement());
8 // write the document to output stream ("out")
9 OutputFormat format = new OutputFormat(doc);
10 XMLSerializer serializer = new XMLSerializer(out, format);
11 serializer.serialize(doc.getDocumentElement());
2 // loop through child nodes
3 List children = element.getContent();
4 for (int i = 0; i < children.size(); i++) {
5 // handle child by node type
6 Object child = children.get(i);
7 if (child instanceof String) {
8 // trim whitespace from content text
9 String trimmed = child.toString().trim();
10 if (trimmed.length() == 0) {
11 // delete child if only whitespace (adjusting index)
12 children.remove(i--);
13 } else {
14 // wrap the trimmed content with new element
15 Element text = new Element("text", element.getNamespace());
16 text.setText(trimmed);
17 children.set(i, text);
18 }
19 } else if (child instanceof Element) {
20 // handle child elements with recursive call
21 modifyElement((Element)child);
2 // loop through child nodes
3 List children = element.content();
4 for (int i = 0; i < children.size(); i++) {
5 // handle child by node type
6 Node child = (Node)children.get(i);
7 if (child.getNodeType() == Node.TEXT_NODE) {
8 // trim whitespace from content text
9 String trimmed = child.getText().trim();
10 if (trimmed.length() == 0) {
11 // delete child if only whitespace (adjusting index)
12 children.remove(i--);
13 } else {
14 // wrap the trimmed content with new element
15 Element text = m_factory.createElement
16 (QName.get("text", element.getNamespace()));
17 text.addText(trimmed);
18 children.set(i, text);
19 }
20 } else if (child.getNodeType() == Node.ELEMENT_NODE) {
21 // handle child elements with recursive call
22 modifyElement((Element)child);
2 // loop through child nodes
3 Child child;
4 Child next = element.getChildren().first();
5 while ((child = next) != null) {
6 // set next before we change anything
7 next = child.getNextSibling();
8 // handle child by node type
9 if (child instanceof Text) {
10 // trim whitespace from content text
11 String trimmed = ((Text)child).getString().trim();
12 if (trimmed.length() == 0) {
13 // delete child if only whitespace
14 child.remove();
15 } else {
16 // wrap the trimmed content with new element
17 Element text = new Element();
18 text.addText(trimmed);
19 child.replaceWith(text);
20 text.setName(element.getPrefix(), "text");
21 }
22 } else if (child instanceof Element) {
23 // handle child elements with recursive call
24 modifyElement((Element)child);
2 // loop through child nodes
3 for (int i = 0; i < element.getChildrenCount(); i++) {
4 // handle child by node type
5 Object child = element.getChildAt(i);
6 if (child instanceof String) {
7 // trim whitespace from content text
8 String trimmed = child.toString().trim();
9 if (trimmed.length() == 0) {
10 // delete child if only whitespace (adjusting index)
11 element.removeChildAt(i--);
12 } else {
13 // construct qualified name for wrapper element
15 String prefix = element.getPrefix();
16 String name = (prefix == null) ? "text" : (prefix + ":text");
17 // wrap the trimmed content with new element
18 XmlNode text = m_parserFactory.newNode();
19 text.appendChild(trimmed);
20 element.replaceChildAt(i, text);
21 text.modifyTag(element.getNamespaceUri(), "text", name);
22 }
23 } else if (child instanceof XmlNode) {
24 // handle child elements with recursive call
25 modifyElement((XmlNode)child);
26 }
27 }
28 }
結束語
DOM、dom4j 和 Electric XML 都得到這些幾乎同樣便於使用的代碼樣本,其中 EXML 可能最簡單,而 dom4j 受一些小條件限制而較困難。DOM 提供了與語言無關的非常實在的好處,但是如果你只使用 Java 代碼,那麼通過與 Java 特定的模型相比較,它看上去有點麻煩。我認為這表明 Java 特定的模型通常成功地實現簡化 Java 代碼中的 XML 文檔處理這個目標。
儘管如此,對於使用多種語言的開發人員來說,DOM 仍是一個非常好的選擇。DOM 實現廣泛應用於多種程式設計語言。它還是許多其它與 XML 相關的標準的基礎,所以即使您使用 Java 特定的模型,也還有一個您逐步熟悉 DOM 所需要的好機會。因為它正式獲得 W3C 推薦(與基於非標準的 Java 模型相對),所以在某些類型的項目中可能也需要它。
就使用方便這一範疇而言,在 JDOM、dom4j 和 Electric XML 這三個主要競爭者中,dom4j 與其它兩個的區別在於它使用帶有多個繼承層的基於介面的方法。這會使得遵循 API JavaDocs 更為困難些。例如,您正在尋找的一個方法(例如 content(),在我們 dom4j 的 modify 方法樣本的第 3 行中使用的)可能是 Element 擴充的 Branch 介面的一部分,而不是 Element 介面本身的一部分。儘管如此,這種基於介面的設計添加了許多靈活性(請參閱側欄超越基礎:真實世界可用性)。考慮到 dom4j 的效能、穩定性和特性設定的優點,您應把它當作多數項目中的一個有力的候選者。
在任一 Java 特定的文檔模型之中,JDOM 可能擁有最廣泛的使用者基礎,並且它的確是使用起來最簡單的模型之一。儘管如此,作為項目開發的一個選擇,它還是必須容忍 API 的不固定性和從一個版本到下一個版本的更新,在效能對比中它也表現得很糟糕。基於當前實現,我願為著手新項目的人們推薦 dom4j,而不是 JDOM。
除了 XPP 以外,EXML 比其它任何模型佔用的資源都要少得多,並且考慮到 EXML 便於使用的優點,您應肯定會認為它適用於 jar 檔案大小很重要的應用程式。但是,EXML 的 XML 支援的局限性和受限的許可證,以及在較大檔案上所表現出的相對拙劣的效能,不得不在許多應用程式中放棄使用它。
XPP 在文法分析和編寫文字文件時需要更多步驟,並且在處理名稱空間時也需要更多步驟。如果 XPP 打算添加一些便利的方法來處理其中一些常見情況,那麼在對比中它可能會更勝一籌。正如它現在所表現的,上篇文章中效能方面的領先者卻成了本文中的可用性方面的失敗者。儘管如此,因為 XPP 效能方面的優勢,所以對於需要較小的 jar 檔案大小的應用程式還是值得將它作為 EXML 的替代方法。
下一次...
到目前為止在我寫的兩篇文章中,涉及到用 Java 編寫的 XML 文檔模型的效能和可用性。在本系列的後兩篇文章中,我將討論用 Java 技術進行 XML 資料繫結的方法。這些方法與文檔模型的方法有許多相似處,但是它們更進一步將 XML 文檔映射到實際應用程式資料結構中。我們將看到這一操作在使用的簡便性和提高效能方面是如何做得如此好的。
回到 developerWorks,檢查 Java 代碼的 XML 資料繫結的實質。同時,您可以通過下面連結的論壇,給出您對本文的評論和問題。
參考資料
單擊本文頂部或底部的討論,參與本文的論壇。
如果您需要背景資料,嘗試 developerWorks 的 XML Java 編程 教程、理解 SAX 教程和 理解 DOM 教程。
從下載頁面下載本文中使用的測試程式和文檔模型庫。
尋找有關 Java APIs for XML Processing (JAXP) 或者閱讀 JAXP Tutorial。
擷取作者有關 XML Streaming 的著作的詳細資料,它作為程式間傳送 XML 文檔的 Java 序列化的另一種選擇。
回顧作者以前的文章:XML in Java: Document models, part 1。
根據 Tony Darugar 的團隊對幾個大型 XML 項目的分析,參考他對 Effective DOM with Java 的建議。
Java 的 XML 文檔模型:
Xerces Java
Crimson
JDOM
dom4j
Electric XML
XML Pull Parser (XPP)
請查看 IBM 的 WebSphere Studio Application Developer,它是一個實現 DOM 的用於 Java、XML 和 Web 服務的整合可視編程環境。
想要提高您的技能嗎?請檢查 XML 認證頁 — IBM 專業教育計劃的一部分。