轉自http://www.ibm.com/developerworks/cn/java/j-jdom/index.html
JDOM 是一種使用 XML 的獨特 Java 工具包,用于快速開發 XML 應用程序。它的設計包含 Java 語言的語法乃至語義。但是它是否比現有的 -- 更標準的 -- XML APL 好呢?當我們看過一些示例并說明這個流行的開放源代碼項目的設計目標后,您自己來判斷吧。最近這個開放源代碼項目已被正式接受成為 Java 規范要求。
作為開發人員,您可能聽說過 80-20 規則,在其它領域被稱為 Pareto 法則:一種過程或方法能適應所有可能情況的 80%,另外的 20% 則需要根據具體情況來處理。軟件開發的必然結果是:對于開發人員而言,有了給定的技術后就能非常容易地完成可能要做的工作的 80%。
當然,軟件產品和標準并不總是根據 80-20 規則發展的。特別的,Java XML 的缺陷就是這條規則的一個例外。Java 的編程世界擁有很多的 API -- 一些是自己開發的,一些是由幾個大公司開發并被制定為標準的 -- 他們提供了解決特殊 XML 任務的成熟解決方案。作為 XML 普遍性的證明,每個新任務都存在著一種新技術,但如何將它們結合在一起,又如何尋找一種合適的工具去完成必須重復做的任務中的 80% -- 利用 Java 語言的直觀映象的基本 XML 樹操作?JDOM 正好是用來解決上述問題的一個 XML API。
標記:Java 和 XML
在許多方面,Java 語言已變成供 XML 選擇的一種編程語言。由于 Apache 軟件基金會和 IBM alphaWorks 所做的開創性工作,現在已有完整的工具鏈用于創建,操作,傳送文檔和對 XML 文檔進行語法分析。
但是,雖然許多 Java 開發人員每天都在使用 XML,Sun 卻在將 XML 整合進 Java 平臺方面落后了。因為在 XML 成為從商家對商家集成到 Web 站點內容流水化等方面的關鍵技術之前,Java 2 平臺就已經非常流行了。Sun 已經使用 JSR 過程使之成為現存 XML API 的鼻祖,這一點已被廣泛接受。目前最顯著的是加入了 JAXP (用于 XML 語法分析的 Java API),其中包含了三個軟件包:
org.w3c.dom
,W3C 推薦的用于 XML 標準規劃文檔對象模型的 Java 工具
org.xml.sax
,用于對 XML 進行語法分析的事件驅動的簡單 API
javax.xml.parsers
,工廠化工具,允許應用程序開發人員獲得并配置特殊的語法分析器工具
盡管對于 Java 開發人員而言,有了這些軟件包是件好事,但它僅僅代表獲得了現有 API 標準的正式許可而已,并沒有在提供一流的 Java-XML 互操作性方面取得了巨大飛躍。核心 Java 平臺所缺乏的是將 XML 文檔作為 Java 對象操作的直觀接口。
進入 JDOM。JDOM 是兩位著名的 Java 開發人員兼作者,Brett Mclaughlin 和 Jason Hunter 的創作成果, 2000 年初在類似于 Apache 協議的許可下,JDOM 作為一個開放源代碼項目正式開始研發,JDOM 作為一個開放源代碼項目正式開始了。它已成長為包含來自廣泛的 Java 開發人員的投稿、集中反饋及錯誤修復的系統,并致力于建立一個完整的基于 Java 平臺的解決方案,通過 Java 代碼來訪問、操作并輸出 XML 數據。
這是 JDOM 適合的 API、啞元
JDOM 能夠替換 org.w3c.dom
軟件包來有計劃地操作 XML 文檔。它并不是一個簡單的替代品,實際上 JDOM 和 DOM 能夠愉快地并存。另外,盡管它提供的類的封裝從配置和運行分析器執行中分擔了大量工作,但它不負責根據文本輸入來對 XML 進行語法分析。JDOM 建立在現有的 API 的能力之上,正如項目網頁所表述的“一個更好的捕鼠器”。
要理解需要備用 API 的原因,就要考慮 W3C DOM 設計的局限性:
- 語言獨立。DOM 并不是用人們心目中的 Java 語言設計的。雖然這種方法保留了在不同語言中非常相似的 API,它也使那些習慣 Java 語言的程序員感到更麻煩。例如:Java 語言內建了一種
String
類,而 DOM 則規范定義了自己的 Text
類。
- 嚴格的層次結構。DOM API 直接沿襲了 XML 規范。在 XML 中,每件東西都是一個結點,因此您能在 DOM 中找到一個幾乎每件東西都可以擴展的基于
Node
的接口和返回 Node
的一系列方法。就多態性的觀點來講,它是優秀的,但鑒于如上解釋,它在 Java 語言中的應用是困難而且不便的,其中從 Node
向葉類型作顯式下拉會導致代碼的冗長和難以理解。
- 接口驅動。公共 DOM API 僅由接口組成(
Exception
類是一個例外,但恰恰足夠了)。w3c 對提供實現并不感興趣,它只對定義接口(比較有意義)感興趣。但它也意味著作為 Java 程序員使用 API 在創建 XML 對象時增加了分散程度,因為 w3c 標準大量使用工廠化的類和類似的靈活的但不直接的模式。在某些應用中,XML 文檔是僅由語法分析器建立的,而從不會由應用程序級代碼建立,這是不相關的。但是,隨著 XML 更廣泛的使用,并不是所有問題都繼續需要由語法分析器來驅動。應用程序的開發人員需要一個更方便的方法有計劃地構造 XML 對象。
對于程序員,這些約束意味著龐大(在內存占用和接口大小方面)的和難掌握的 API,學習和使用都很難。相反,JDOM 是作為一種輕量級 API 被制定的,最主要的是它是以 Java 為中心的。它在遵循 DOM 主要規則的基礎上除去了上述缺點:
- JDOM 是 Java 平臺專用的。只要有可能,API 都使用 Java 語言的內建
String
支持,因此文本值也適用于 String
。它還可利用 Java 2 平臺的類集,如 List
和 Iterator
,給程序員提供了一個豐富的并且和 Java 語言類似的環境。
- 沒有層次性。在 JDOM 中,XML 元素就是
Element
的實例,XML 屬性就是 Attribute
的實例,XML 文檔本身就是 Document
的實例。由于在 XML 中所有這些都代表了不同的概念,因此它們總是作為自己的類型被引用,而不是作為一個含糊的“結點”。
- 類驅動。因為 JDOM 對象就是像
Document
、 Element
和 Attribute
這些類的直接實例,因此創建一個新 JDOM 對象就如在 Java 語言中使用 new
操作符一樣容易。它還意味著不需要進行工廠化接口配置 -- JDOM 的使用是直截了當的。
看,沒有 Node
:建立和操作 JDOM 文檔
JDOM 使用標準的 Java 編碼模式。只要有可能,它使用 Java new
操作符而不用復雜的工廠化模式,使對象操作即便對于初學用戶也很方便。例如,讓我們看一下如何隨便使用 JDOM 建立一個簡單的 XML 文檔。我們將要建立的結構如清單 1 所示。(從 參考資料上可下載關于本文的完整代碼)
清單 1. 建立 XML 文檔樣本
<?xml version="1.0" encoding="UTF-8"?>
<car vin="123fhg5869705iop90">
<!--Description of a car-->
<make>Toyota</make>
<model>Celica</model>
<year>1997</year>
<color>green</color>
<license state="CA">1ABC234</license>
</car>
|
注意:我們將建立 示例文檔,在下面的清單 2 到清單 7 中有詳細描述。
開始,讓我們先創建一個根元素,并將其添加到文檔中:
清單 2. 創建一個 Document
Element carElement = new Element("car");
Document myDocument = new Document(carElement);
|
這一步創建一個新 org.jdom.Element
,并將其作為 org.jdom.Document
myDocument
的根元素。(如果您使用 參考資料中提供的樣本代碼,請務必導入 org.jdom.*
。)因為一個 XML 文檔必須一直有一個唯一的根元素,所以 Document
將 Element
放在它的構造器中。
下一步,添加 vin
屬性:
清單 3. 添加一個 Attribute
carElement.addAttribute(new Attribute("vin", "123fhg5869705iop90"));
|
添加元素也是很簡單的。這里我們添加 make
元素:
清單 4. 元素和子元素
Element make = new Element("make");
make.addContent("Toyota");
carElement.addContent(make);
|
由于 Element
的 addContent
方法返回 Element
,我們也可以這樣寫:
清單 5. 用簡潔形式添加元素
carElement.addContent(new Element("make").addContent("Toyota"));
|
這兩個語句完成了相同的工作。有些人認為第一個示例可讀性更好,但是如果您一次建立許多元素,您會覺得第二個示例可讀性更好。要完成構建文檔:
清單 6. 添加其余的元素
carElement.addContent(new Element("model").addContent("Celica"));
carElement.addContent(new Element("year").addContent("1997"));
carElement.addContent(new Element("color").addContent("green"));
carElement.addContent(new Element("license")
.addContent("1ABC234").addAttribute("state", "CA"));
|
您會注意到對于 license
元素,我們不但添加了元素的內容,還為其添加了一個屬性,表明許可已被發出了這個狀態。這是因為 Element
的 addContent
方法總是返回 Element
本身,而不是一個無效的聲明。
用同樣的方法添加注釋部分或其它標準 XML 類型:
清單 7. 添加一條注釋
carElement.addContent(new Comment("Description of a car"));
|
操作文檔也是用類似方式。例如,要引用 year
元素,我們使用 Element
的 getChild
方法:
清單 8. 訪問子元素
Element yearElement = carElement.getChild("year");
|
該語句實際上將返回第一個元素名為 year
的子 Element
。 如果沒有 year
元素,則調用返回一個空值。注意,我們不必回溯來自任何類似于 DOM Node
接口的返回值 -- Element
的子元素就是 Element
。用類似的方式,我們可把 year
元素從文檔中除去:
清單 9. 除去子元素
boolean removed = carElement.removeChild("year");
|
這次調用將只除去 year
元素;文檔的其余部分保持不變。
到目前為止,我們已經涵蓋了文檔的生成和操作。要將完成的文檔輸出至控制臺,可使用 JDOM 的 XMLOutputter
類:
清單 10. 將 JDOM 轉化為 XML 文本
try {
XMLOutputter outputter = new XMLOutputter(" ", true);
outputter.output(myDocument, System.out);
} catch (java.io.IOException e) {
e.printStackTrace();
}
|
XMLOutputter
有幾個格式選項。這里我們已指定希望子元素從父元素縮進兩個空格,并且希望元素間有空行。 XMLOutputter
可輸出到 Writer
或 OutputStream
。為輸出到文件,我們可以簡單地將輸出行簡化為:
清單 11. 使用 FileWriter
輸出 XML
FileWriter writer = new FileWriter("/some/directory/myFile.xml");
outputter.output(myDocument, writer);
writer.close();
|
與其它方法良好協作:和現有的 XML 工具進行互操作
JDOM 的一個有趣特征是和其它 API 有互操作性。使用 JDOM,不僅能把文檔輸出到 Stream
或 Reader
,還可將文檔作為 SAX Event Stream
或作為 DOM Document
。這種靈活性允許 JDOM 能在多種環境下使用或被添加到已經在使用另一種方法處理 XML 的系統中去。正如我們在后面一個示例中所看到的,它還允許 JDOM 使用其它的還不能識別 JDOM 的數據結構的 XML 工具。
JDOM 的另一個用處是它能夠讀取并操作現有的 XML 數據。使用 org.jdom.input
中的一個類可以閱讀結構很規范的 XML 文件。在這個示例中我們使用 SAXBuilder
:
清單 12. 使用 SAXBuilder
對 XML 文件進行語法分析
try {
SAXBuilder builder = new SAXBuilder();
Document anotherDocument =
builder.build(new File("/some/directory/sample.xml"));
} catch(JDOMException e) {
e.printStackTrace();
} catch(NullPointerException e) {
e.printStackTrace();
}
|
您可以用清單 2 到清單 7 中顯示的方法來操作通過這個過程建立的文檔。
JDOM 的另一個實用應用程序將其與 Apache 的 Xalan 產品結合在一起(請參閱 參考資料)。使用上面的汽車示例,我們將為在線汽車經銷商建立一個 Web 頁面,顯示特定汽車的詳細信息。首先,假設我們上面建立的文檔顯示我們準備呈現給用戶的汽車的信息。下一步,我們將把這個 JDOM Document
與一個 XSL 樣式表結合起來并把 HTML 格式的結果輸出到 servlet 的 OutputStream
上以便在用戶的瀏覽器中顯示。
在本例中,我們準備使用的 XSL 樣式表被稱為 car.xsl
:
清單 13. 用于將汽車記錄轉換為 HTML 的 XSL 文檔
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/car">
<html>
<head>
<title><xsl:value-of select="make"/> <xsl:value-of select="model"/>
</head>
<body>
<h1><xsl:value-of select="make"/></h1><br />
<h2><xsl:value-of select="model"/></h2><br />
<table border="0">
<tr><td>VIN:</td><td><xsl:value-of select="@vin"/></td></tr>
<tr><td>Year:</td><td><xsl:value-of select="year"/></td></tr>
<tr><td>Color:</td><td><xsl:value-of select="color"/></td></tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
|
現在我們將把 org.jdom.Document
轉換為 DOM Document
,并將其與顯示我們的 XSL 和 OutputStream
的文件一起提供給 Xalan, OutputStream
是我們從我們假定的使用 servlet(如清單 14 所示)的應用服務器上獲取的。
清單 14. 使用 JDOM 和 Xalan 創建 HTML 文檔
TransformerFactory tFactory = TransformerFactory.newInstance();
// Make the input sources for the XML and XSLT documents
org.jdom.output.DOMOutputter outputter = new org.jdom.output.DOMOutputter();
org.w3c.dom.Document domDocument = outputter.output(myDocument);
javax.xml.transform.Source xmlSource =
new javax.xml.transform.dom.DOMSource(domDocument);
StreamSource xsltSource =
new StreamSource(new FileInputStream("/some/directory/car.xsl"));
// Make the output result for the finished document using
// the HTTPResponse OutputStream
StreamResult xmlResult = new StreamResult(response.getOutputStream());
// Get a XSLT transformer
Transformer transformer = tFactory.newTransformer(xsltSource);
// Do the transform
transformer.transform(xmlSource, xmlResult);
|
在這個示例中,輸出是通過 Java servlet 的 HTTPResponse
OutputStream
流出。然而,輸出流可以象早期的使用 XMLOutputter
的實例一樣簡單的通過文件流輸出。我們使用 DOMOutputter
為 Xalan 生成 XML 源代碼。但是我們可以生成相同的輸出,方法是使用 XMLOutputter
將我們的 XML 文檔作為 String
輸出并使其進入 StreamSource
。說到靈活性:JDOM 可將它的結構作為 String
、SAX Event Stream
或 DOM Document
輸出。這允許 JDOM 與能把任何這些模型作為輸入的工具一起工作。(關于附加功能,請訪問 JDOM Web 站點的 contrib
包,在那里您將發現一個基于 JDOM 工具的寶庫,可提供基于 JDBC ResultSet 的構建器、XPATH 實現方法和其它更多工具。)
在短短幾行代碼中,JDOM 啟用了許多功能,我們已經在 XML 中分析過并有計劃地創建了 XML 文檔,操作了那些文檔,并使用它們產生 XML 驅動的 Web 頁面。
</script>