一、JDOM簡介
JDOM是一個開源項目,它基于樹型結構,利用純JAVA的技術對XML文檔實現解析、生成、序列化以及多種操作。
JDOM直接為JAVA編程服務。它利用更為強有力的JAVA語言的諸多特性(方法重載、集合概念以及映射),把SAX和DOM的功能有效地結合起來。
在使用設計上盡可能地隱藏原來使用XML過程中的復雜性。利用JDOM處理XML文檔將是一件輕松、簡單的事。
JDOM在2000年的春天被BrettMcLaughlin和JasonHunter開發出來,以彌補DOM及SAX在實際應用當中的不足之處。
這些不足之處主要在于SAX沒有文檔修改、隨機訪問以及輸出的功能,而對于DOM來說,JAVA程序員在使用時來用起來總覺得不太方便。
DOM的缺點主要是來自于由于Dom是一個接口定義語言(IDL),它的任務是在不同語言實現中的一個最低的通用標準,并不是為JAVA特別設計的。JDOM的最新版本為JDOMBeta9。最近JDOM被收錄到JSR-102內,這標志著JDOM成為了JAVA平臺組成的一部分。
二、JDOM包概覽
JDOM是由以下幾個包組成的
org.jdom 包含了所有的xml文檔要素的java類
org.jdom.adapters 包含了與dom適配的java類
org.jdom.filter 包含了xml文檔的過濾器類
org.jdom.input 包含了讀取xml文檔的類
org.jdom.output 包含了寫入xml文檔的類
org.jdom.transform 包含了將jdomxml文檔接口轉換為其他xml文檔接口
org.jdom.xpath 包含了對xml文檔xpath操作的類三、JDOM類說明
1、org.JDOM這個包里的類是你J解析xml文件后所要用到的所有數據類型。
Attribute
CDATA
Coment
DocType
Document
Element
EntityRef
Namespace
ProscessingInstruction
Text
2、org.JDOM.transform在涉及xslt格式轉換時應使用下面的2個類
JDOMSource
JDOMResult
org.JDOM.input
3、輸入類,一般用于文檔的創建工作
SAXBuilder
DOMBuilder
ResultSetBuilder
org.JDOM.output
4、輸出類,用于文檔轉換輸出
XMLOutputter
SAXOutputter
DomOutputter
JTreeOutputter
使用前注意事項:
1.JDOM對于JAXP以及TRax的支持
JDOM支持JAXP1.1:你可以在程序中使用任何的parser工具類,默認情況下是JAXP的parser。
制定特別的parser可用如下形式
SAXBuilderparser
=newSAXBuilder("org.apache.crimson.parser.XMLReaderImpl");
Documentdoc=parser.build("http://www.cafeconleche.org/");
//workwiththedocument...
JDOM也支持TRaX:XSLT可通過JDOMSource以及JDOMResult類來轉換(參見以后章節)
2.注意在JDOM里文檔(Document)類由org.JDOM.Document來表示。這要與org.w3c.dom中的Document區別開,這2種格式如何轉換在后面會說明。
以下如無特指均指JDOM里的Document。
四、JDOM主要使用方法
1.Ducument類
(1)Document的操作方法:
Elementroot=newElement("GREETING");
Documentdoc=newDocument(root);
root.setText("HelloJDOM!");
或者簡單的使用Documentdoc=newDocument(newElement("GREETING").setText("HelloJDOM!t"));
這點和DOM不同。Dom則需要更為復雜的代碼,如下:
DocumentBuilderFactoryfactory=DocumentBuilderFactory.newInstance();
DocumentBuilderbuilder=factory.newDocumentBuilder();
Documentdoc=builder.newDocument();
Elementroot=doc.createElement("root");
Texttext=doc.createText("Thisistheroot");
root.appendChild(text);
doc.appendChild(root);
注意事項:JDOM不允許同一個節點同時被2個或多個文檔相關聯,要在第2個文檔中使用原來老文檔中的節點的話。首先需要使用detach()把這個節點分開來。
(2)從文件、流、系統ID、URL得到Document對象:
DOMBuilderbuilder=newDOMBuilder();
Documentdoc=builder.build(newFile("jdom_test.xml"));
SAXBuilderbuilder=newSAXBuilder();
Documentdoc=builder.build(url);
在新版本中DOMBuilder已經Deprecated掉DOMBuilder.builder(url),用SAX效率會比較快。
這里舉一個小例子,為了簡單起見,使用String對象直接作為xml數據源:
publicjdomTest(){
StringtextXml=null;
textXml="<note>";
textXml=textXml+
"<to>aaa</to><from>bbb</from><heading>ccc</heading><body>ddd</body>";
textXml=textXml+"</note>";
SAXBuilderbuilder=newSAXBuilder();
Documentdoc=null;
Readerin=newStringReader(textXml);
try{
doc=builder.build(in);
Elementroot=doc.getRootElement();
Listls=root.getChildren();//注意此處取出的是root節點下面的一層的Element集合
for(Iteratoriter=ls.iterator();iter.hasNext();){
Elementel=(Element)iter.next();
if(el.getName().equals("to")){
System.out.println(el.getText());
}
}
}
catch(IOExceptionex){
ex.printStackTrace();
}
catch(JDOMExceptionex){
ex.printStackTrace();
}
}
(3)DOM的document和JDOM的Document之間的相互轉換使用方法,簡單!
DOMBuilderbuilder=newDOMBuilder();
org.jdom.DocumentjdomDocument=builder.build(domDocument);
DOMOutputterconverter=newDOMOutputter();//workwiththeJDOMdocument…
org.w3c.dom.DocumentdomDocument=converter.output(jdomDocument);
//workwiththeDOMdocument…
2.XML文檔輸出
XMLOutPutter類:
JDOM的輸出非常靈活,支持很多種io格式以及風格的輸出
Documentdoc=newDocument(...);
XMLOutputteroutp=newXMLOutputter();
outp.output(doc,fileOutputStream);//Rawoutput
outp.setTextTrim(true);//Compressedoutput
outp.output(doc,socket.getOutputStream());
outp.setIndent("");//Prettyoutput
outp.setNewlines(true);
outp.output(doc,System.out);
詳細請參閱最新的JDOMAPI手冊
3.Element類:
(1)瀏覽Element樹
Elementroot=doc.getRootElement();//獲得根元素element
ListallChildren=root.getChildren();//獲得所有子元素的一個list
ListnamedChildren=root.getChildren("name");//獲得指定名稱子元素的list
Elementchild=root.getChild("name");//獲得指定名稱的第一個子元素
JDOM給了我們很多很靈活的使用方法來管理子元素(這里的List是java.util.List)
ListallChildren=root.getChildren();
allChildren.remove(3);//刪除第四個子元素
allChildren.removeAll(root.getChildren("jack"));//刪除叫“jack”的子元素
root.removeChildren("jack");//便捷寫法
allChildren.add(newElement("jane"));//加入
root.addContent(newElement("jane"));//便捷寫法
allChildren.add(0,newElement("first"));
(2)移動Elements:
在JDOM里很簡單
Elementmovable=newElement("movable");
parent1.addContent(movable);//place
parent1.removeContent(movable);//remove
parent2.addContent(movable);//add
在Dom里
Elementmovable=doc1.createElement("movable");
parent1.appendChild(movable);//place
parent1.removeChild(movable);//remove
parent2.appendChild(movable);//出錯!
補充:糾錯性
JDOM的Element構造函數(以及它的其他函數)會檢查element是否合法。
而它的add/remove方法會檢查樹結構,檢查內容如下:
1.在任何樹中是否有回環節點
2.是否只有一個根節點
3.是否有一致的命名空間(Namespaces)
(3)Element的text內容讀取
<description>
Acooldemo
</description>
//Thetextisdirectlyavailable
//Returns"\nAcooldemo\n"
Stringdesc=element.getText();
//There'saconvenientshortcut
//Returns"Acooldemo"
Stringdesc=element.getTextTrim();
(4)Elment內容修改
element.setText("Anewdescription");
3.可正確解釋特殊字符
element.setText("<xml>content");
4.CDATA的數據寫入、讀出
element.addContent(newCDATA("<xml>content"));
StringnoDifference=element.getText();
混合內容
element可能包含很多種內容,比如說
<table>
<!--Somecomment-->
Sometext
<tr>Somechildelement</tr>
</table>
取table的子元素tr
Stringtext=table.getTextTrim();
Elementtr=table.getChild("tr");
也可使用另外一個比較簡單的方法
ListmixedCo=table.getContent();
Iteratoritr=mixedCo.iterator();
while(itr.hasNext()){
Objecto=i.next();
if(oinstanceofComment){...}
//這里可以寫成Comment,Element,Text,CDATA,ProcessingInstruction,或者是EntityRef的類型
}
//現在移除Comment,注意這里游標應為1。這是由于回車鍵也被解析成Text類的緣故,所以Comment項應為1。
mixedCo.remove(1);
4.Attribute類
<tablewidth="100%"border="0"></table>
Stringwidth=table.getAttributeValue("width");//獲得attribute
intborder=table.getAttribute("width").getIntValue();
table.setAttribute("vspace","0");//設置attribute
table.removeAttribute("vspace");//刪除一個或全部attribute
table.getAttributes().clear();
5.處理指令(ProcessingInstructions)操作
一個Pls的例子
<?br?>
<?cocoon-processtype="xslt"?>
| |
| |
目標 數據
處理目標名稱(Target)
Stringtarget=pi.getTarget();
獲得所有數據(data),在目標(target)以后的所有數據都會被返回。
Stringdata=pi.getData();
Stringtype=pi.getValue("type");獲得指定屬性的數據
Listls=pi.getNames();獲得所有屬性的名稱
6.命名空間操作
<xhtml:html
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xhtml:title>HomePage</xhtml:title>
</xhtml:html>
Namespacexhtml=Namespace.getNamespace("xhtml","http://www.w3.org/1999/xhtml");
Listkids=html.getChildren("title",xhtml);
Elementkid=html.getChild("title",xhtml);
kid.addContent(newElement("table",xhtml));
7.XSLT格式轉換
使用以下函數可對XSLT轉換
最后如果你需要使用w3c的Document則需要轉換一下。
publicstaticDocumenttransform(Stringstylesheet,Documentin)
throwsJDOMException{
try{
Transformertransformer=TransformerFactory.newInstance()
.newTransformer(newStreamSource(stylesheet));
JDOMResultout=newJDOMResult();
transformer.transform(newJDOMSource(in),out);
returnout.getDeocument();
}
catch(TransformerExceptione){
thrownewJDOMException("XSLTTrandformationfailed",e);
}
}
五、用例:
1、生成xml文檔:
publicclassWriteXML{
publicvoidBuildXML()throwsException{
Elementroot,student,number,name,age;
root=newElement("student-info");//生成根元素:student-info
student=newElement("student");//生成元素:student(number,name,age)
number=newElement("number");
name=newElement("name");
age=newElement("age");
Documentdoc=newDocument(root);//將根元素植入文檔doc中
number.setText("001");
name.setText("lnman");
age.setText("24");
student.addContent(number);
student.addContent(name);
student.addContent(age);
root.addContent(student);
Formatformat=Format.getCompactFormat();
format.setEncoding("gb2312");//設置xml文件的字符為gb2312
format.setIndent(" ");//設置xml文件的縮進為4個空格
XMLOutputterXMLOut=newXMLOutputter(format);//元素后換行一層元素縮四格
XMLOut.output(doc,newFileOutputStream("studentinfo.xml"));
}
publicstaticvoidmain(String[]args)throwsException{
WriteXMLw=newWriteXML();
System.out.println("NowwebuildanXMLdocument.....");
w.BuildXML();
System.out.println("finished!");
}
}
生成的xml文檔為:
<?xmlversion="1.0"encoding="gb2312"?>
<student-info>
<student>
<number>001</number>
<name>lnman</name>
<age>24</age>
</student>
</student-info>
創建XML文檔2:
publicclassCreateXML{
publicvoidCreate(){
try{
Documentdoc=newDocument();
ProcessingInstructionpi=newProcessingInstruction("xml-stylesheet","type="text/xsl"href="test.xsl"");
doc.addContent(pi);
Namespacens=Namespace.getNamespace("http://www.bromon.org");
Namespacens2=Namespace.getNamespace("other","http://www.w3c.org");
Elementroot=newElement("根元素",ns);
root.addNamespaceDeclaration(ns2);
doc.setRootElement(root);
Elementel1=newElement("元素一");
el1.setAttribute("屬性","屬性一");
Texttext1=newText("元素值");
Elementem=newElement("元素二").addContent("第二個元素");
el1.addContent(text1);
el1.addContent(em);
Elementel2=newElement("元素三").addContent("第三個元素");
root.addContent(el1);
root.addContent(el2);
//縮進四個空格,自動換行,gb2312編碼
XMLOutputteroutputter=newXMLOutputter(" ",true,"GB2312");
outputter.output(doc,newFileWriter("test.xml"));
}catch(Exceptione) {
System.out.println(e);
}
}
publicstaticvoidmain(Stringargs[]){
newCreateXML().Create();
}
}
2、讀取xml文檔的例子:
importorg.jdom.output.*;
importorg.jdom.input.*;
importorg.jdom.*;
importjava.io.*;
importjava.util.*;
publicclassReadXML{
publicstaticvoidmain(String[]args)throwsException{
SAXBuilderbuilder=newSAXBuilder();
Documentread_doc=builder.build("studentinfo.xml");
Elementstu=read_doc.getRootElement();
Listlist=stu.getChildren("student");
for(inti=0;i<list.size();i++){
Elemente=(Element)list.get(i);
Stringstr_number=e.getChildText("number");
Stringstr_name=e.getChildText("name");
Stringstr_age=e.getChildText("age");
System.out.println("---------STUDENT--------------");
System.out.println("NUMBER:"+str_number);
System.out.println("NAME:"+str_name);
System.out.println("AGE:"+str_age);
System.out.println("------------------------------");
System.out.println();
}
}
}
3、DTD驗證的:
publicclassXMLWithDTD{
publicvoidvalidate() {
try{
SAXBuilderbuilder=newSAXBuilder(true);
builder.setFeature("http://xml.org/sax/features/validation";,true);
Documentdoc=builder.build(newFileReader("author.xml"));
System.out.println("搞掂");
XMLOutputteroutputter=newXMLOutputter();
outputter.output(doc,System.out);
}catch(Exceptione){
System.out.println(e);
}
}
publicstaticvoidmain(Stringargs[]){
newXMLWithDTD().validate();
}
}
需要說明的是,這個程序沒有指明使用哪個DTD文件。DTD文件的位置是在XML中指定的,而且DTD不支持命名空間,一個XML只能引用一個DTD,所以程序直接讀取XML中指定的DTD,程序本身不用指定。不過這樣一來,好象就只能使用外部式的DTD引用方式了?高人指點。
4、XMLSchema驗證的:
publicclassXMLWithSchema{
Stringxml="test.xml";
Stringschema="test-schema.xml";
publicvoidvalidate(){
try{
SAXBuilderbuilder=newSAXBuilder(true);
//指定約束方式為XMLschema
builder.setFeature("http://apache.org/xml/features/validation/schema";, true);
//導入schema文件
builder.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation";,schema);
Documentdoc=builder.build(newFileReader(xml));
System.out.println("搞掂");
XMLOutputteroutputter=newXMLOutputter();
outputter.output(doc,System.out);
}catch(Exceptione){
System.out.println("驗證失敗:"+e);
}
}
}
上面的程序就指出了要引入的XMLSchema文件的位置。
系統默認輸出是UTF-8,這有可能導致出現亂碼。
5、Xpath例子:
JDOM的關于XPATH的api在org.jdom.xpath這個包里。這個包下,有一個抽象類XPath.java和實現類JaxenXPath.java,使用時先用XPath類的靜態方法newInstance(Stringxpath)得到XPath對象,然后調用它的selectNodes(Objectcontext)方法或selectSingleNode(Objectcontext)方法,前者根據xpath語句返回一組節點(List對象);后者根據一個xpath語句返回符合條件的第一個節點(Object類型)。請看jdom-1.0自帶的范例程序:
它分析在web.xml文件中的注冊的servlet的個數及參數個數,并輸出角色名。
web.xml文件:
<?xmlversion="1.0"encoding="ISO-8859-1"?>
<!--
<!DOCTYPEweb-app
PUBLIC"-//SunMicrosystems,Inc.//DTDWebApplication2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
-->
<web-app>
<servlet>
<servlet-name>snoop</servlet-name>
<servlet-class>SnoopServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>file</servlet-name>
<servlet-class>ViewFile</servlet-class>
<init-param>
<param-name>initial</param-name>
<param-value>1000</param-value>
<description>Theinitialvalueforthecounter <!--optional--></description>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mv</servlet-name>
<url-pattern>*.wm</url-pattern>
</servlet-mapping>
<distributed/>
<security-role>
<role-name>manager</role-name>
<role-name>director</role-name>
<role-name>president</role-name>
</security-role>
</web-app>
處理程序:
importjava.io.*;
importjava.util.*;
publicclassXPathReader{
publicstaticvoidmain(String[]args)throwsIOException,JDOMException{
if(args.length!=1){
System.err.println("Usage:javaXPathReaderweb.xml");
return;
}
Stringfilename=args[0];//從命令行輸入web.xml
PrintStreamout=System.out;
SAXBuilderbuilder=newSAXBuilder();
Documentdoc=builder.build(newFile(filename));//得到Document對象
//Printservletinformation
XPathservletPath=XPath.newInstance("http://servlet");//,選擇任意路徑下servlet元素
Listservlets=servletPath.selectNodes(doc);//返回所有的servlet元素。
out.println("ThisWARhas"+servlets.size()+"registeredservlets:");
Iteratori=servlets.iterator();
while(i.hasNext()){//輸出servlet信息
Elementservlet=(Element)i.next();
out.print("\t"+servlet.getChild("servlet-name")
.getTextTrim()+
"for"+servlet.getChild("servlet-class")
.getTextTrim());
ListinitParams=servlet.getChildren("init-param");
out.println("(ithas"+initParams.size()+"initparams)");
}
//Printsecurityroleinformation
XPathrolePath=XPath.newInstance("http://security-role/role-name/text()");
ListroleNames=rolePath.selectNodes(doc);//得到所有的角色名
if(roleNames.size()==0){
out.println("ThisWARcontainsnoroles");
}else{
out.println("ThisWARcontains"+roleNames.size()+"roles:");
i=roleNames.iterator();
while(i.hasNext()){//輸出角色名
out.println("\t"+((Text)i.next()).getTextTrim());
}
}
}
}
輸出結果:
C:\java>java XPathReaderweb.xml
ThisWARhas2registeredservlets:
snoopforSnoopServlet(ithas0initparams)
fileforViewFile(ithas1initparams)
ThisWARcontains3roles:
manager
director
president
6、數據輸入要用到XML文檔要通過org.jdom.input包,反過來需要org.jdom.output。如前面所說,關是看API文檔就能夠使用。
我們的例子讀入XML文件exampleA.xml,加入一條處理指令,修改第一本書的價格和作者,并添加一條屬性,然后寫入文件exampleB.xml:
//exampleA.xml
<?xmlversion="1.0"encoding="GBK"?>
<bookList>
<book>
<name>Java編程入門</name>
<author>張三</author>
<publishDate>2002-6-6</publishDate>
<price>35.0</price>
</book>
<book>
<name>XML在Java中的應用</name>
<author>李四</author>
<publishDate>2002-9-16</publishDate>
<price>92.0</price>
</book>
</bookList>
//testJDOM.java
importorg.jdom.*;
importorg.jdom.output.*;
importorg.jdom.input.*;
importjava.io.*;
publicclassTestJDOM{
publicstaticvoidmain(Stringargs[])throwsException{
SAXBuildersb=newSAXBuilder();
//從文件構造一個Document,因為XML文件中已經指定了編碼,所以這里不必了
Documentdoc=sb.build(newFileInputStream("exampleA.xml"));
ProcessingInstructionpi=newProcessingInstruction//加入一條處理指令
("xml-stylesheet","href=\"bookList.html.xsl\"type=\"text/xsl\"");
doc.addContent(pi);
Elementroot=doc.getRootElement();//得到根元素
java.util.Listbooks=root.getChildren();//得到根元素所有子元素的集合
Elementbook=(Element)books.get(0);//得到第一個book元素
//為第一本書添加一條屬性
Attributea=newAttribute("hot","true");
book.setAttribute(a);
Elementauthor=book.getChild("author");//得到指定的字元素
author.setText("王五");//將作者改為王五
//或Textt=newText("王五");book.addContent(t);
Elementprice=book.getChild("price");//得到指定的字元素
//修改價格,比較郁悶的是我們必須自己轉換數據類型,而這正是JAXB的優勢
author.setText(Float.toString(50.0f));
Stringindent="";
booleannewLines=true;
XMLOutputteroutp=newXMLOutputter(indent,newLines,"GBK");
outp.output(doc,newFileOutputStream("exampleB.xml"));
}
};
執行結果exampleB.xml:
<?xmlversion="1.0"encoding="GBK"?>
<bookList>
<bookhot=”true”>
<name>Java編程入門</name>
<author>50.0</author>
<publishDate>2002-6-6</publishDate>
<price>35.0</price>
</book>
<book>
<name>XML在Java中的應用</name>
<author>李四</author>
<publishDate>2002-9-16</publishDate>
<price>92.0</price>
</book>
</bookList>
<?xml-stylesheethref="bookList.html.xsl"type="text/xsl"?>
在默認情況下,JDOM的Element類的getText()這類的方法不會過濾空白字符,如果你需要過濾,用setTextTrim()