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