一、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() 。