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文件后所要用到的所有数据类型。AttributeCDATAComentDocTypeDocumentElementEntityRefNamespaceProscessingInstructionText2 、org.JDOM.transform在涉及xslt格式转换时应使用下面的2个类JDOMSourceJDOMResultorg.JDOM.input3 、输入类,一般用于文档的创建工作SAXBuilderDOMBuilderResultSetBuilderorg.JDOM.output4 、输出类,用于文档转换输出XMLOutputterSAXOutputterDomOutputterJTreeOutputter使用前注意事项: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 " A cool demo " 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的子元素trString 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("//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(" " + 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("//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(" " + ((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() 。