一、JDOM解析
特征:
1、仅使用具体类,而不使用接口。
2、API大量使用了Collections类。
Jdom由6个包构成:
- Element类表示XML文档的元素
- org.jdom: 解析xml文件所要用到的基础类
- org.jdom.adapters: 包含DOM适配的Java类
- org.jdom.filter: 包含xml文档的过滤类
- org.jdom.input: 包含读取XML文档的Java类
- org.jdom.output: 包含输出XML文档的类
- org.jdom.trans form: 包含将Jdom xml文档接口转换为其他XML文档接口的Java类
以下是实例:
- 下载jdom包,解压文件jdom-2.0.6.jar,将包导入项目。
- 使用jdom创建一个xml文件,名字为people.xml
package cn.XmlFile; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; //生成xml文件 public class CreateJdom { public static void main(String[] args) { //定义元素 Element people,student; people = new Element("people"); student = new Element("student"); //设置属性 student.setAttribute("name", "张三"); student.setAttribute("salary","8000"); //设置文本 student.setText("呵呵"); //将其添加到根目录下 people.addContent(student); //新建一个文档。 Document doc = new Document(people); //读取格式,赋值给当前的Format Format format = Format.getCompactFormat(); //对当前格式进行初始化 format.setEncoding("UTF-8"); //设置xml文件缩进4个空格 format.setIndent(" "); //建一个xml输出工厂,将格式给工厂 XMLOutputter xmlout = new XMLOutputter(format); try { //将其写好的文本给工厂,并且建一个文件输出流,将数据输出 xmlout.output(doc, new FileOutputStream("D:\MyPractice01\sources\people.xml")); System.out.println("成功!"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
- 使用Jdom解析people.xml文件
package cn.XmlFile; import java.io.IOException; import java.util.List; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; //读取people.xml文档 public class ReadxmlByJDOM { public static void main(String[] args) { //新建构造器解析xml SAXBuilder sax = new SAXBuilder(); //建一个文档去接受数据 Document doc; try { //获取people.xml文档 doc = sax.build("D:\MyPractice01\sources\people.xml"); //获得根节点 Element people = doc.getRootElement(); //获得根节点下的节点数据 List<Element> list = people.getChildren(); for(int i = 0;i<list.size();i++){ Element e = list.get(i); //获得属性值 System.out.println("name:"+e.getAttributeValue("name")+" salary:"+e.getAttributeValue("salary")); //获得文本值 System.out.println(e.getText()); } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
二、DOM4J解析
DOM4J是dom4j.org出品的一个开源XML解析包,它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
DOM4J使用起来非常简单。只要你了解基本的XML-DOM模型,就能使用。DOM4J最大的特色是使用大量的接口,这也是它被认为比JDOM灵活的主要原因。
特征:
- JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功能。
- 它使用接口和抽象基本类方法。
- 具有性能优异、灵活性好、功能强大和极端易用的特点。
- 是一个开放源码的文件。
它的主要接口都在org.dom4j这个包里定义:
1、读取并解析XML文档:
读写XML文档主要依赖于org.dom4j.io包,其中提供DOMReader和SAXReader两类不同方式,而调用方式是一样的。这就是依靠接口的好处。
// 从文件读取XML,输入文件名,返回XML文档 public Document read(String fileName) throws MalformedURLException, DocumentException { SAXReader reader = new SAXReader(); Document document = reader.read(new File(fileName)); return document; }
其中,reader的read方法是重载的,可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。
注意,读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题,注意要把各处的编码名称保持一致即可。
2、 取得Root节点
读取后的第二步,就是得到Root节点,一切XML分析都是从Root元素开始的。
public Element getRootElement(Document doc){ return doc.getRootElement(); }
3、 遍历XML树
DOM4J提供至少3种遍历节点的方法:
1) 枚举(Iterator)
// 枚举所有子节点 for ( Iterator i = root.elementIterator(); i.hasNext(); ) { Element element = (Element) i.next(); // do something } // 枚举名称为foo的节点 for ( Iterator i = root.elementIterator(foo); i.hasNext();) { Element foo = (Element) i.next(); // do something } // 枚举属性 for ( Iterator i = root.attributeIterator(); i.hasNext(); ) { Attribute attribute = (Attribute) i.next(); // do something }
2)递归
递归也可以采用Iterator作为枚举手段,但文档中提供了另外的做法。
public void treeWalk() { treeWalk(getRootElement()); } public void treeWalk(Element element) { for (int i = 0, size = element.nodeCount(); i < size; i++) { Node node = element.node(i); if (node instanceof Element) { treeWalk((Element) node); } else { // do something.... } } }
3) Visitor模式
最令人兴奋的是DOM4J对Visitor的支持,这样可以大大缩减代码量,并且清楚易懂。了解设计模式的人都知道,Visitor是GOF设计模式之一。其主要原理就是两种类互相保有对方的引用,并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供) 。
只需要自定一个类实现Visitor接口即可。
public class MyVisitor extends VisitorSupport { public void visit(Element element){ System.out.println(element.getName()); } public void visit(Attribute attr){ System.out.println(attr.getName()); } }
调用:root.accept(new MyVisitor())
Visitor接口提供多种Visit()的重载,根据XML不同的对象,将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现,一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器,Visitor接口的Default Adapter模式,这个模式给出了各种visit(*)的空实现,以便简化代码。注意,这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor),将遍历子节点。
4、 XPath支持
DOM4J对XPath有良好的支持,如访问一个节点,可直接用XPath选择。
public void bar(Document document) { List list = document.selectNodes( //foo/bar ); Node node = document.selectSingleNode(//foo/bar/author); String name = node.valueOf( @name ); }
例如,如果你想查找XHTML文档中所有的超链接,下面的代码可以实现。
public void findLinks(Document document) throws DocumentException { List list = document.selectNodes( //a/@href ); for (Iterator iter = list.iterator(); iter.hasNext(); ) { Attribute attribute = (Attribute) iter.next(); String url = attribute.getValue(); } }
5、 字符串与XML的转换
// XML转字符串 Document document = ...; String text = document.asXML(); // 字符串转XML String text = James ; Document document = DocumentHelper.parseText(text);
6 、用XSLT转换XML
public Document styleDocument( Document document, String stylesheet ) throws Exception { // load the transformer using JAXP TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer( new StreamSource( stylesheet ) ); // now lets style the given document DocumentSource source = new DocumentSource( document ); DocumentResult result = new DocumentResult(); transformer.transform( source, result ); // return the transformed document Document transformedDoc = result.getDocument(); return transformedDoc; }
7、 创建XML
一般创建XML是写文件前的工作,这就像StringBuffer一样容易。
public Document createDocument() { Document document = DocumentHelper.createDocument(); Element root = document.addElement(root); Element author1 = root .addElement(author) .addAttribute(name, James) .addAttribute(location, UK) .addText(James Strachan); Element author2 = root .addElement(author) .addAttribute(name, Bob) .addAttribute(location, US) .addText(Bob McWhirter); return document; }
8、文件输出
一个简单的输出方法是将一个Document或任何的Node通过write方法输出。
FileWriter out = new FileWriter( foo.xml ); document.write(out);
如果你想改变输出的格式,比如美化输出或缩减格式,可以用XMLWriter类。
public void write(Document document) throws IOException { // 指定文件 XMLWriter writer = new XMLWriter( new FileWriter( output.xml ) ); writer.write( document ); writer.close(); // 美化格式 OutputFormat format = OutputFormat.createPrettyPrint(); writer = new XMLWriter( System.out, format ); writer.write( document ); // 缩减格式 format = OutputFormat.createCompactFormat(); writer = new XMLWriter( System.out, format ); writer.write( document ); }
9、操作XML文档
1)建立一个XML文档
/** * 建立一个XML文档,文档名由输入属性决定 * @param filename 需建立的文件名 * @return 返回操作结果, 0表失败, 1表成功 */ public int createXMLFile(String filename){ /** 返回操作结果, 0表失败, 1表成功 */ int returnValue = 0; /** 建立document对象,定义一个XML文档对象。*/ Document document = DocumentHelper.createDocument(); /** 建立XML文档的根books */ Element booksElement = document.addElement("books"); /** 加入一行注释 */ booksElement.addComment("This is a test for dom4j, holen, 2004.9.11"); /** 加入第一个book节点,这句定义一个XML元素,这里添加的是根节点*/ Element bookElement = booksElement.addElement("book"); /** 加入show属性内容 */ bookElement.addAttribute("show","yes"); /** 加入title节点 */ Element titleElement = bookElement.addElement("title"); /** 为title设置内容 */ titleElement.setText("Dom4j Tutorials"); /** 类似的完成后两个book */ bookElement = booksElement.addElement("book"); bookElement.addAttribute("show","yes"); titleElement = bookElement.addElement("title"); titleElement.setText("Lucene Studing"); bookElement = booksElement.addElement("book"); bookElement.addAttribute("show","no"); titleElement = bookElement.addElement("title"); titleElement.setText("Lucene in Action"); /** 加入owner节点 */ Element ownerElement = booksElement.addElement("owner"); ownerElement.setText("O'Reilly"); try{ /** 将document中的内容写入文件中。通过XMLWriter生成物理文件,默认生成的XML文件排版格式比较乱,可以通过OutputFormat类的createCompactFormat()方法或createPrettyPrint()方法 * 格式化输出,默认采用createCompactFormat()方法,显示比较紧凑 */ XMLWriter writer = new XMLWriter(new FileWriter(new File(filename))); writer.write(document); writer.close(); /** 执行成功,需返回1 */ returnValue = 1; }catch(Exception ex){ ex.printStackTrace(); } return returnValue; }
2)修改XML文件中内容
/** * 修改XML文件中内容,并另存为一个新文件 * 重点掌握dom4j中如何添加节点,修改节点,删除节点 * @param filename 修改对象文件 * @param newfilename 修改后另存为该文件 * @return 返回操作结果, 0表失败, 1表成功 */ public int ModiXMLFile(String filename,String newfilename){ int returnValue = 0; try{ SAXReader saxReader = new SAXReader(); Document document = saxReader.read(new File(filename)); /** 修改内容之一: 如果book节点中show属性的内容为yes,则修改成no */ /**通过xpath查找到相应内容。通过setValue()、setText()修改节点内容。通过remove()删除节点或属性。*/
List list = document.selectNodes("/books/book/@show" ); Iterator iter = list.iterator(); while(iter.hasNext()){ Attribute attribute = (Attribute)iter.next(); if(attribute.getValue().equals("yes")){ attribute.setValue("no"); } } /**
* 修改内容之二: 把owner项内容改为Tshinghua * 并在owner节点中加入date节点,date节点的内容为2004-09-11,还为date节点添加一个属性type */ list = document.selectNodes("/books/owner" ); iter = list.iterator(); if(iter.hasNext()){ Element ownerElement = (Element)iter.next(); ownerElement.setText("Tshinghua"); Element dateElement = ownerElement.addElement("date"); dateElement.setText("2004-09-11"); dateElement.addAttribute("type","Gregorian calendar"); } /** 修改内容之三: 若title内容为Dom4j Tutorials,则删除该节点 */ list = document.selectNodes("/books/book"); iter = list.iterator(); while(iter.hasNext()){ Element bookElement = (Element)iter.next(); Iterator iterator = bookElement.elementIterator("title"); while(iterator.hasNext()){ Element titleElement=(Element)iterator.next(); if(titleElement.getText().equals("Dom4j Tutorials")){ bookElement.remove(titleElement); } } } try{ /** 将document中的内容写入文件中 */ XMLWriter writer = new XMLWriter(new FileWriter(new File(newfilename))); writer.write(document); writer.close(); /** 执行成功,需返回1 */ returnValue = 1; }catch(Exception ex){ ex.printStackTrace(); } }catch(Exception ex){ ex.printStackTrace(); } return returnValue; }
3)格式化输出和指定编码
默认的输出方式为紧凑方式,默认编码为UTF-8,但对于我们的应用而言,一般都要用到中文,并且希望显示时按自动缩进的方式的显示,这就需用到OutputFormat类。
/** * 格式化XML文档,并解决中文问题 * @param filename * @return */ public int formatXMLFile(String filename){ int returnValue = 0; try{ SAXReader saxReader = new SAXReader(); Document document = saxReader.read(new File(filename)); XMLWriter writer = null; /** 格式化输出,类型IE浏览一样 */ OutputFormat format = OutputFormat.createPrettyPrint(); /** 指定XML编码 */ format.setEncoding("GBK"); writer= new XMLWriter(new FileWriter(new File(filename)),format); writer.write(document); writer.close(); /** 执行成功,需返回1 */ returnValue = 1; }catch(Exception ex){ ex.printStackTrace(); } return returnValue; }
以下是实例代码:
student.xml:
<?xml version="1.0" encoding="utf-8"?> <students> <student age="25"> <name>张三</name> <college>信息学院</college> <telphone>13610262187</telphone> <notes>男,1982年生,硕士,现就读于北京邮电大学</notes> </student> <student > <name>李四</name> <college leader="leader">PC学院</college> <telphone>13610262187</telphone> <notes>男,1983年生,硕士,现就读于中国农业大学</notes> </student> </students>
StudentDom4j.java:
package cn.DOM4JDemo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.List; import org.dom4j.DocumentException; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.dom4j.Document; import org.dom4j.Element; /* * 使用Dom4j解析XML文档 */ public class StudentDom4j { private Document document; public void getDom(File file){ //创建SAXReader creates a DOM4J tree from SAX parsing events. 创建SAX解析器 SAXReader sax=new SAXReader(); try { //生成DOM树 document=sax.read(file); } catch (DocumentException e) { e.printStackTrace(); } } // 读取student.xml文件中的信息 public void showXML(File file){ //获取XML的根节点 Element root=document.getRootElement(); System.out.println("Root:"+root.getName()); //获取所有子元素 List<Element> childlist=root.elements(); System.out.println("total child count:"+childlist.size()); //获取特定名称的子元素 List<Element> student=root.elements("student"); System.out.println(student.size()); for(Iterator it=student.iterator();it.hasNext();){ Element studentele=(Element)it.next(); String age=studentele.attributeValue("age"); if(age!=null){ System.out.println("<"+studentele.getName()+" "+"age="+studentele.attributeValue("age")+">"); }else{ System.out.println("<"+studentele.getName()+">"); } //取name的文本 List<Element> names=studentele.elements("name"); for(Iterator nameit=names.iterator();nameit.hasNext();){ Element name=(Element)nameit.next(); System.out.println(" "+"name="+name.getText()); } //取college的文本值 List<Element> colleges=studentele.elements("college"); for(Iterator collegeit=colleges.iterator();collegeit.hasNext();){ Element college=(Element)collegeit.next(); //判断有没有属性,如果有就取属性值isTextOnly()如果这个元素只有文本内容则返回true String leader=college.attributeValue("leader"); String text=college.getText(); if(leader!=null){ System.out.println(" "+college.getName()+" "+"leader="+leader); }else{ System.out.println(" "+"college:"+college.getText()); } } //获取telphone文本值 List<Element> tel=studentele.elements("telphone"); for(Iterator telit=tel.iterator();telit.hasNext();){ Element telele=(Element) telit.next(); System.out.println(" "+"telphone:"+telele.getText()); } //获取notes文本值 List<Element> notes=studentele.elements("notes"); for(Iterator noteit=notes.iterator();noteit.hasNext();){ Element noteele=(Element) noteit.next(); System.out.println(" noteele:"+noteele.getText()); } } } //保存 public void saveXML(File tofile){ // 以XML格式输出createPrettyPrint()创建默认的打印格式 OutputFormat of=OutputFormat.createPrettyPrint(); //设置编码 of.setEncoding("utf-8"); try { //以XML格式输出到dom4j.xml中 XMLWriter writer=new XMLWriter(new FileOutputStream(tofile),of); //把源树DOM树输出写进dom4j.xml中 writer.write(document); writer.close(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //修改XML元素 public void updateEle(){ //先要获取到age //得到根元素 Element root=document.getRootElement(); //得到 student元素 List<Element> students=root.elements("student"); //修改<student age="25">的age属性为22 for(Iterator stuit=students.iterator();stuit.hasNext();){ Element stu=(Element) stuit.next(); String age=stu.attributeValue("age"); if(age!=null){ //就是要替换的,修改属性值 stu.attribute("age").setText("22"); } //修改<name>李四</name> 的name文本为sb List<Element> names=stu.elements("name"); for(Iterator name=names.iterator();name.hasNext();){ Element nameele=(Element) name.next(); String nametext=nameele.getText(); if(nametext.equals("李四")){ //替换 nameele.setText("sb"); } } //修改<college leader="leader">的leader值为否 List<Element> colleges=stu.elements("college"); for(Iterator collegeit=colleges.iterator();collegeit.hasNext();){ Element college=(Element) collegeit.next(); String leader=college.attributeValue("leader"); if(leader!=null){ //替换 college.attribute("leader").setText("否"); } } } } //新增元素 public void addEle(){ /* <student > <name>杜和雨</name> <college leader="leader">PC学院</college> </student>*/ //创建根节点 Element root=document.getRootElement(); //创建student标签 Element student=root.addElement("student"); //创建student子标签name Element name=student.addElement("name"); //给name添加文本 name.setText("杜和雨"); //创建student的子标签college Element college=student.addElement("college"); //给college添加文本值 college.addAttribute("leader", "big boss"); } // 删除元素 public void deleteEle(){ /* <student > <name>李四</name> <college leader="leader">PC学院</college> <telphone>13610262187</telphone> <notes>男,1983年生,硕士,现就读于中国农业大学</notes> </student>*/ //得到 根节点 Element root=document.getRootElement(); //找到 student节点 List<Element> students=root.elements(); //删除第1个student节点的所有子节点 //students.remove(0); //删除college的属性leader for(Iterator<Element> stuit=students.iterator();stuit.hasNext();){ Element stu=stuit.next(); List<Element> colleges=stu.elements(); for(Iterator<Element> collit=colleges.iterator();collit.hasNext();){ Element coll=collit.next(); String leader=coll.attributeValue("leader"); if(leader!=null){ coll.remove(coll.attribute("leader")); } } } } // 主函数,测试 public static void main(String[] args){ StudentDom4j sd=new StudentDom4j(); File file=new File("D:\MyPractice01\sources\student.xml"); File tofile=new File("D:\MyPractice01\sources\dom4j_student.xml"); sd.getDom(file); sd.showXML(file); sd.updateEle(); sd.addEle(); sd.deleteEle(); sd.saveXML(tofile); } }
三、总结
DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J。
JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM。虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。