JDOM使用詳解及實例
JDOM使用詳解及實例 一、JDOM?簡介JDOM是一個開源項目,它基于樹型結構,利用純JAVA的技術對XML文檔實現解析、生成、序列化以及多種操作。
JDOM?直接為JAVA編程服務。它利用更為強有力的JAVA語言的諸多特性(方法重載、集合概念以及映射),把SAX和DOM的功能有效地結合起來。
在使用設計上盡可能地隱藏原來使用XML過程中的復雜性。利用JDOM處理XML文檔將是一件輕松、簡單的事。
JDOM?在2000年的春天被Brett?McLaughlin和Jason?Hunter開發出來,以彌補DOM及SAX在實際應用當中的不足之處。
這些不足之處主要在于SAX沒有文檔修改、隨機訪問以及輸出的功能,而對于DOM來說,JAVA程序員在使用時來用起來總覺得不太方便。
DOM的缺點主要是來自于由于Dom是一個接口定義語言(IDL),它的任務是在不同語言實現中的一個最低的通用標準,并不是為JAVA特別設計的。JDOM的最新版本為JDOM?Beta?9。最近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????????包含了將jdom?xml文檔接口轉換為其他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可用如下形式
SAXBuilder?parser?
??=?new?SAXBuilder("org.apache.crimson.parser.XMLReaderImpl");
?Document?doc?=?parser.build("http://www.cafeconleche.org/");
?//?work?with?the?document...
JDOM也支持TRaX:XSLT可通過JDOMSource以及JDOMResult類來轉換(參見以后章節)
2.注意在JDOM里文檔(Document)類由org.JDOM.Document?來表示。這要與org.w3c.dom中的Document區別開,這2種格式如何轉換在后面會說明。
以下如無特指均指JDOM里的Document。
四、JDOM主要使用方法
1.Ducument類
(1)Document的操作方法:
Element?root?=?new?Element("GREETING");
Document?doc?=?new?Document(root);
root.setText("Hello?JDOM!");
或者簡單的使用Document?doc?=?new?Document(new?Element("GREETING").setText("Hello?JDOM!t"));
這點和DOM不同。Dom則需要更為復雜的代碼,如下:
DocumentBuilderFactory?factory?=DocumentBuilderFactory.newInstance();
DocumentBuilder?builder?=factory.newDocumentBuilder();
Document?doc?=?builder.newDocument();
Element?root?=doc.createElement("root");
Text?text?=?doc.createText("This?is?the?root");
root.appendChild(text);
doc.appendChild(root);
注意事項:JDOM不允許同一個節點同時被2個或多個文檔相關聯,要在第2個文檔中使用原來老文檔中的節點的話。首先需要使用detach()把這個節點分開來。
(2)從文件、流、系統ID、URL得到Document對象:
DOMBuilder?builder?=?new?DOMBuilder();
Document?doc?=?builder.build(new?File("jdom_test.xml"));
SAXBuilder?builder?=?new?SAXBuilder();
Document?doc?=?builder.build(url);
在新版本中DOMBuilder?已經Deprecated掉?DOMBuilder.builder(url),用SAX效率會比較快。
這里舉一個小例子,為了簡單起見,使用String對象直接作為xml數據源:
?public?jdomTest()?{
????String?textXml?=?null;
????textXml?=?"<note>";
????textXml?=?textXml?+
????????"<to>aaa</to><from>bbb</from><heading>ccc</heading><body>ddd</body>";
????textXml?=?textXml?+?"</note>";
????SAXBuilder?builder?=?new?SAXBuilder();
????Document?doc?=?null;
????Reader?in=?new?StringReader(textXml);
????try?{
??????doc?=?builder.build(in);
??????Element?root?=?doc.getRootElement();
??????List?ls?=?root.getChildren();//注意此處取出的是root節點下面的一層的Element集合
??????for?(Iterator?iter?=?ls.iterator();?iter.hasNext();?)?{
????????Element?el?=?(Element)?iter.next();
????????if(el.getName().equals("to")){
?????????System.out.println(el.getText());
????????}
??????}
????}
????catch?(IOException?ex)?{
??????ex.printStackTrace();
????}
????catch?(JDOMException?ex)?{
??????ex.printStackTrace();
????}
??}
(3)DOM的document和JDOM的Document之間的相互轉換使用方法,簡單!
DOMBuilder?builder?=?new?DOMBuilder();
org.jdom.Document?jdomDocument?=?builder.build(domDocument);
DOMOutputter?converter?=?new?DOMOutputter();//?work?with?the?JDOM?document…
org.w3c.dom.Document?domDocument?=?converter.output(jdomDocument);
//?work?with?the?DOM?document…
2.XML文檔輸出
XMLOutPutter類:
JDOM的輸出非常靈活,支持很多種io格式以及風格的輸出
Document?doc?=?new?Document(...);
XMLOutputter?outp?=?new?XMLOutputter();
outp.output(doc,?fileOutputStream);?//?Raw?output
outp.setTextTrim(true);?//?Compressed?output
outp.output(doc,?socket.getOutputStream());
outp.setIndent("?");//?Pretty?output
outp.setNewlines(true);
outp.output(doc,?System.out);
詳細請參閱最新的JDOM?API手冊
3.Element?類:
(1)瀏覽Element樹
Element?root?=?doc.getRootElement();//獲得根元素element
List?allChildren?=?root.getChildren();//?獲得所有子元素的一個list
List?namedChildren?=?root.getChildren("name");//?獲得指定名稱子元素的list
Element?child?=?root.getChild("name");//獲得指定名稱的第一個子元素
JDOM給了我們很多很靈活的使用方法來管理子元素(這里的List是java.util.List)
List?allChildren?=?root.getChildren();
allChildren.remove(3);?//?刪除第四個子元素
allChildren.removeAll(root.getChildren("jack"));//?刪除叫“jack”的子元素
root.removeChildren("jack");?//?便捷寫法
allChildren.add(new?Element("jane"));//?加入
root.addContent(new?Element("jane"));?//?便捷寫法
allChildren.add(0,?new?Element("first"));
(2)移動Elements:
在JDOM里很簡單
Element?movable?=?new?Element("movable");
parent1.addContent(movable);?//?place
parent1.removeContent(movable);?//?remove
parent2.addContent(movable);?//?add
在Dom里
Element?movable?=?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>
A?cool?demo
</description>
//?The?text?is?directly?available
//?Returns?"\n?A?cool?demo\n"
String?desc?=?element.getText();
//?There's?a?convenient?shortcut
//?Returns?"A?cool?demo"
String?desc?=?element.getTextTrim();
(4)Elment內容修改
element.setText("A?new?description");
3.可正確解釋特殊字符
element.setText("<xml>?content");
4.CDATA的數據寫入、讀出
element.addContent(new?CDATA("<xml>?content"));
String?noDifference?=?element.getText();
混合內容
element可能包含很多種內容,比如說
<table>
<!--?Some?comment?-->
Some?text
<tr>Some?child?element</tr>
</table>
取table的子元素tr
String?text?=?table.getTextTrim();
Element?tr?=?table.getChild("tr");
也可使用另外一個比較簡單的方法
List?mixedCo?=?table.getContent();
Iterator?itr?=?mixedCo.iterator();
while?(itr.hasNext())?{
Object?o?=?i.next();
if?(o?instanceof?Comment)?{...}
//?這里可以寫成Comment,?Element,?Text,?CDATA,ProcessingInstruction,?或者是EntityRef的類型
}
//?現在移除Comment,注意這里游標應為1。這是由于回車鍵也被解析成Text類的緣故,所以Comment項應為1。
mixedCo.remove(1);?
4.Attribute類
<table?width="100%"?border="0">?</table>
String?width?=?table.getAttributeValue("width");//獲得attribute
int?border?=?table.getAttribute("width").getIntValue();
table.setAttribute("vspace",?"0");//設置attribute
table.removeAttribute("vspace");//?刪除一個或全部attribute
table.getAttributes().clear();?
5.處理指令(Processing?Instructions)操作
一個Pls的例子
<?br?>
<?cocoon-process?type="xslt"?>
??????????|????????|
??????????|????????|
????????目標?????數據
處理目標名稱(Target)
String?target?=?pi.getTarget();
獲得所有數據(data),在目標(target)以后的所有數據都會被返回。
String?data?=?pi.getData();
String?type?=?pi.getValue("type");獲得指定屬性的數據
List?ls?=?pi.getNames();獲得所有屬性的名稱
6.命名空間操作
<xhtml:html?
?xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xhtml:title>Home?Page</xhtml:title>
</xhtml:html>
Namespace?xhtml?=?Namespace.getNamespace("xhtml",?"http://www.w3.org/1999/xhtml");
List?kids?=?html.getChildren("title",?xhtml);
Element?kid?=?html.getChild("title",?xhtml);
kid.addContent(new?Element("table",?xhtml));
7.XSLT格式轉換
使用以下函數可對XSLT轉換
最后如果你需要使用w3c的Document則需要轉換一下。
public?static?Document?transform(String?stylesheet,Document?in)
????????????????????????????????????????throws?JDOMException?{
?????try?{
???????Transformer?transformer?=?TransformerFactory.newInstance()
?????????????????????????????.newTransformer(new?StreamSource(stylesheet));
???????JDOMResult?out?=?new?JDOMResult();
???????transformer.transform(new?JDOMSource(in),?out);
???????return?out.getDeocument();
?????}
?????catch?(TransformerException?e)?{
???????throw?new?JDOMException("XSLT?Trandformation?failed",?e);
?????}
???}
五、用例:
1、生成xml文檔:
public?class?WriteXML{
????public?void?BuildXML()?throws?Exception?{
????????Element?root,student,number,name,age;?????????
????????root?=?new?Element("student-info");?//生成根元素:student-info
????????student?=?new?Element("student");?//生成元素:student(number,name,age)?????????????????????????????
????????number?=?new?Element("number");
????????name?=?new?Element("name");
????????age?=?new?Element("age");
????????Document?doc?=?new?Document(root);?//將根元素植入文檔doc中
????????number.setText("001");
????????name.setText("lnman");
????????age.setText("24");
????????student.addContent(number);
????????student.addContent(name);
????????student.addContent(age);
????????root.addContent(student);
????????Format?format?=?Format.getCompactFormat();
????????format.setEncoding("gb2312");?//設置xml文件的字符為gb2312
????????format.setIndent("????");?//設置xml文件的縮進為4個空格
????????XMLOutputter?XMLOut?=?new?XMLOutputter(format);//元素后換行一層元素縮四格?
????????XMLOut.output(doc,?new?FileOutputStream("studentinfo.xml"));??
}
????public?static?void?main(String[]?args)?throws?Exception?{
????????WriteXML?w?=?new?WriteXML();
????????System.out.println("Now?we?build?an?XML?document?.....");
????????w.BuildXML();
????????System.out.println("finished!");
}
}
生成的xml文檔為:
<?xml?version="1.0"?encoding="gb2312"?>
<student-info>
????<student>
????????<number>001</number>
????????<name>lnman</name>
????????<age>24</age>
????</student>
</student-info>
??
創建XML文檔2:
?public?class?CreateXML?{
??public?void?Create()?{
???try?{
????Document?doc?=?new?Document();???
????ProcessingInstruction?pi=new?ProcessingInstruction("xml-stylesheet","type="text/xsl"?href="test.xsl"");
????doc.addContent(pi);????
????Namespace?ns?=?Namespace.getNamespace("http://www.bromon.org"?);
????Namespace?ns2?=?Namespace.getNamespace("other",?"http://www.w3c.org"?);
????Element?root?=?new?Element("根元素",?ns);
????root.addNamespaceDeclaration(ns2);
????doc.setRootElement(root);
????Element?el1?=?new?Element("元素一");
????el1.setAttribute("屬性",?"屬性一");????
????Text?text1=new?Text("元素值");
?????????????Element?em?=?new?Element("元素二").addContent("第二個元素");
????el1.addContent(text1);
?????????????el1.addContent(em);?????????????
?????????????Element?el2?=?new?Element("元素三").addContent("第三個元素");
?????????????root.addContent(el1);
?????????????root.addContent(el2);?????????????
?????????????//縮進四個空格,自動換行,gb2312編碼
?????????????XMLOutputter?outputter?=?new?XMLOutputter("??",?true,"GB2312");
?????????????outputter.output(doc,?new?FileWriter("test.xml"));
?????????}catch(Exception?e)??{
??????????System.out.println(e);
?????????}
?????}?????
?????public?static?void?main(String?args[])?{
??????new?CreateXML().Create();
?????}?????
?}
2、讀取xml文檔的例子:
import?org.jdom.output.*;
import?org.jdom.input.*;
import?org.jdom.*;
import?java.io.*;
import?java.util.*;
public?class?ReadXML{
????public?static?void?main(String[]?args)?throws?Exception?{
????????SAXBuilder?builder?=?new?SAXBuilder();
????????Document?read_doc?=?builder.build("studentinfo.xml");
????????Element?stu?=?read_doc.getRootElement();
????????List?list?=?stu.getChildren("student");
????????for(int?i?=?0;i?<?list.size();i++)?{
????????????Element?e?=?(Element)list.get(i);
????????????String?str_number?=?e.getChildText("number");
????????????String?str_name?=?e.getChildText("name");
????????????String?str_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驗證的:
?public?class?XMLWithDTD?{
??public?void?validate()??{
???try?{
????SAXBuilder?builder?=?new?SAXBuilder(true);
????builder.setFeature("http://xml.org/sax/features/validation";,true);?
????Document?doc?=?builder.build(new?FileReader("author.xml"));????
????System.out.println("搞掂");
????XMLOutputter?outputter?=?new?XMLOutputter();
????outputter.output(doc,?System.out);
???}catch(Exception?e)?{
????System.out.println(e);
???}???
??}
??public?static?void?main(String?args[])?{
???new?XMLWithDTD().validate();
??}??
?}
? 需要說明的是,這個程序沒有指明使用哪個DTD文件。DTD文件的位置是在XML中指定的,而且DTD不支持命名空間,一個XML只能引用一個DTD,所以程序直接讀取XML中指定的DTD,程序本身不用指定。不過這樣一來,好象就只能使用外部式的DTD引用方式了?高人指點。
??
4、XML?Schema驗證的:
?public?class?XMLWithSchema?{
??String?xml="test.xml";
??String?schema="test-schema.xml";
??public?void?validate()?{
???try?{
????SAXBuilder?builder?=?new?SAXBuilder(true);
????//指定約束方式為XML?schema
????builder.setFeature("http://apache.org/xml/features/validation/schema";,??true);
????//導入schema文件
builder.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation";,schema);?
????Document?doc?=?builder.build(new?FileReader(xml));????
????System.out.println("搞掂");
????XMLOutputter?outputter?=?new?XMLOutputter();
????outputter.output(doc,?System.out);
???}catch(Exception?e)?{
????System.out.println("驗證失敗:"+e);
???}??
??}?
?}
?上面的程序就指出了要引入的XML?Schema文件的位置。
?系統默認輸出是UTF-8,這有可能導致出現亂碼。
5、Xpath例子:
JDOM的關于XPATH的api在org.jdom.xpath這個包里。這個包下,有一個抽象類XPath.java和實現類JaxenXPath.java,?使用時先用XPath類的靜態方法newInstance(String?xpath)得到XPath對象,然后調用它的selectNodes(Object?context)方法或selectSingleNode(Object?context)方法,前者根據xpath語句返回一組節點(List對象);后者根據一個xpath語句返回符合條件的第一個節點(Object類型)。請看jdom-1.0自帶的范例程序:?
?????它分析在web.xml文件中的注冊的servlet的個數及參數個數,并輸出角色名。?
web.xml文件:?
<?xml?version="1.0"?encoding="ISO-8859-1"?>?
<!--?
<!DOCTYPE?web-app?
????PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.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>The?initial?value?for?the?counter??<!--?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>?
處理程序:?
import?java.io.*;?
import?java.util.*;??
public?class?XPathReader?{??????
????public?static?void?main(String[]?args)?throws?IOException,?JDOMException?{?
????????if?(args.length?!=?1)?{?
????????????System.err.println("Usage:?java?XPathReader?web.xml");?
????????????return;?
????????}?
????????String?filename?=?args[0];//從命令行輸入web.xml?
????????PrintStream?out?=?System.out;?
????????SAXBuilder?builder?=?new?SAXBuilder();?
????????Document?doc?=?builder.build(new?File(filename));//得到Document對象?
????????//?Print?servlet?information?
????????XPath?servletPath?=?XPath.newInstance("http://servlet");//,選擇任意路徑下servlet元素?
????????List?servlets?=?servletPath.selectNodes(doc);//返回所有的servlet元素。
????????out.println("This?WAR?has?"+?servlets.size()?+"?registered?servlets:");?
????????Iterator?i?=?servlets.iterator();?
????????while?(i.hasNext())?{//輸出servlet信息?
????????????Element?servlet?=?(Element)?i.next();?
????????????out.print("\t"?+?servlet.getChild("servlet-name")?
????????????????????????????????????.getTextTrim()?+?
??????????????????????"?for?"?+?servlet.getChild("servlet-class")?
???????????????????????????????????????.getTextTrim());?
????????????List?initParams?=?servlet.getChildren("init-param");?
????????????out.println("?(it?has?"?+?initParams.size()?+?"?init?params)");??
????????}??????????????
????????//?Print?security?role?information?
????????XPath?rolePath?=?XPath.newInstance("http://security-role/role-name/text()");?
????????List?roleNames?=?rolePath.selectNodes(doc);//得到所有的角色名?
????????if?(roleNames.size()?==?0)?{?
????????????out.println("This?WAR?contains?no?roles");?
????????}?else?{?
????????????out.println("This?WAR?contains?"?+?roleNames.size()?+?"?roles:");?
????????????i?=?roleNames.iterator();?
????????????while?(i.hasNext())?{//輸出角色名?
????????????????out.println("\t"?+?((Text)i.next()).getTextTrim());?
????????????}?
????????}?
????}?????
}?
輸出結果:?
C:\java>java???XPathReader?web.xml?
This?WAR?has?2?registered?servlets:?
????????snoop?for?SnoopServlet?(it?has?0?init?params)?
????????file?for?ViewFile?(it?has?1?init?params)?
This?WAR?contains?3?roles:?
????????manager?
????????director?
????????president
6、數據輸入要用到XML文檔要通過org.jdom.input包,反過來需要org.jdom.output。如前面所說,關是看API文檔就能夠使用。?
我們的例子讀入XML文件exampleA.xml,加入一條處理指令,修改第一本書的價格和作者,并添加一條屬性,然后寫入文件exampleB.xml:?
//exampleA.xml?
<?xml?version="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?
import?org.jdom.*;?
import?org.jdom.output.*;?
import?org.jdom.input.*;?
import?java.io.*;?
public?class?TestJDOM{?
public?static?void?main(String?args[])throws?Exception{?
SAXBuilder?sb?=?new?SAXBuilder();?
//從文件構造一個Document,因為XML文件中已經指定了編碼,所以這里不必了?
Document?doc?=?sb.build(new?FileInputStream("exampleA.xml"));?
ProcessingInstruction?pi?=?new?ProcessingInstruction//加入一條處理指令?
("xml-stylesheet","href=\"bookList.html.xsl\"?type=\"text/xsl\"");?
doc.addContent(pi);?
Element?root?=?doc.getRootElement();?//得到根元素?
java.util.List?books?=?root.getChildren();?//得到根元素所有子元素的集合?
Element?book?=?(Element)books.get(0);?//得到第一個book元素?
//為第一本書添加一條屬性?
Attribute?a?=?new?Attribute("hot","true");?
book.setAttribute(a);?
Element?author?=?book.getChild("author");?//得到指定的字元素?
author.setText("王五");?//將作者改為王五?
//或?Text?t?=?new?Text("王五");book.addContent(t);?
Element?price?=?book.getChild("price");?//得到指定的字元素?
//修改價格,比較郁悶的是我們必須自己轉換數據類型,而這正是JAXB的優勢?
author.setText(Float.toString(50.0f));?
String?indent?=?"?";?
boolean?newLines?=?true;?
XMLOutputter?outp?=?new?XMLOutputter(indent,newLines,"GBK");?
outp.output(doc,?new?FileOutputStream("exampleB.xml"));?
}?
};?
執行結果exampleB.xml:?
<?xml?version="1.0"?encoding="GBK"?>?
<bookList>?
<book?hot=”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-stylesheet?href="bookList.html.xsl"?type="text/xsl"?>?
在默認情況下,JDOM的Element類的getText()這類的方法不會過濾空白字符,如果你需要過濾,用setTextTrim()?。