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