java解析xml汇总_动力节点Java学院整理
【引言】
目前在java中用于解析xml的技术很多,主流的有dom、sax、jdom、dom4j,下文主要介绍这4种解析xml文档技术的使用、优缺点及性能测试。
一、【基础知识——扫盲】
sax、dom是两种对xml文档进行解析的方法(没有具体实现,只是接口),所以只有它们是无法解析xml文档的;jaxp只是api,它进一步封装了sax、dom两种接口,并且提供了domcumentbuilderfactory/domcumentbuilder和saxparserfactory/saxparser(默认使用xerces解释器)。
二、【dom、sax、jdom、dom4j简单使用介绍】
1、【dom(document object model) 】
由w3c提供的接口,它将整个xml文档读入内存,构建一个dom树来对各个节点(node)进行操作。
示例代码:
<?xml version="1.0" encoding="utf-8"?> <university name="pku"> <college name="c1"> <class name="class1"> <student name="stu1" sex='male' age="21" /> <student name="stu2" sex='female' age="20" /> <student name="stu3" sex='female' age="20" /> </class> <class name="class2"> <student name="stu4" sex='male' age="19" /> <student name="stu5" sex='female' age="20" /> <student name="stu6" sex='female' age="21" /> </class> </college> <college name="c2"> <class name="class3"> <student name="stu7" sex='male' age="20" /> </class> </college> <college name="c3"> </college> </university>
后文代码中有使用到text.xml(该文档放在src路径下,既编译后在classes路径下),都是指该xml文档。
package test.xml; import java.io.file; import java.io.filenotfoundexception; import java.io.fileoutputstream; import java.io.ioexception; import java.io.inputstream; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import javax.xml.transform.transformer; import javax.xml.transform.transformerconfigurationexception; import javax.xml.transform.transformerexception; import javax.xml.transform.transformerfactory; import javax.xml.transform.dom.domsource; import javax.xml.transform.stream.streamresult; import org.w3c.dom.document; import org.w3c.dom.element; import org.w3c.dom.node; import org.w3c.dom.nodelist; import org.w3c.dom.text; import org.xml.sax.saxexception; /** * dom读写xml * @author whwang */ public class testdom { public static void main(string[] args) { read(); //write(); } public static void read() { documentbuilderfactory dbf = documentbuilderfactory.newinstance(); try { documentbuilder builder = dbf.newdocumentbuilder(); inputstream in = testdom.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.parse(in); // root <university> element root = doc.getdocumentelement(); if (root == null) return; system.err.println(root.getattribute("name")); // all college node nodelist collegenodes = root.getchildnodes(); if (collegenodes == null) return; for(int i = 0; i < collegenodes.getlength(); i++) { node college = collegenodes.item(i); if (college != null && college.getnodetype() == node.element_node) { system.err.println("\t" + college.getattributes().getnameditem("name").getnodevalue()); // all class node nodelist classnodes = college.getchildnodes(); if (classnodes == null) continue; for (int j = 0; j < classnodes.getlength(); j++) { node clazz = classnodes.item(j); if (clazz != null && clazz.getnodetype() == node.element_node) { system.err.println("\t\t" + clazz.getattributes().getnameditem("name").getnodevalue()); // all student node nodelist studentnodes = clazz.getchildnodes(); if (studentnodes == null) continue; for (int k = 0; k < studentnodes.getlength(); k++) { node student = studentnodes.item(k); if (student != null && student.getnodetype() == node.element_node) { system.err.print("\t\t\t" + student.getattributes().getnameditem("name").getnodevalue()); system.err.print(" " + student.getattributes().getnameditem("sex").getnodevalue()); system.err.println(" " + student.getattributes().getnameditem("age").getnodevalue()); } } } } } } } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } public static void write() { documentbuilderfactory dbf = documentbuilderfactory.newinstance(); try { documentbuilder builder = dbf.newdocumentbuilder(); inputstream in = testdom.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.parse(in); // root <university> element root = doc.getdocumentelement(); if (root == null) return; // 修改属性 root.setattribute("name", "tsu"); nodelist collegenodes = root.getchildnodes(); if (collegenodes != null) { for (int i = 0; i <collegenodes.getlength() - 1; i++) { // 删除节点 node college = collegenodes.item(i); if (college.getnodetype() == node.element_node) { string collegename = college.getattributes().getnameditem("name").getnodevalue(); if ("c1".equals(collegename) || "c2".equals(collegename)) { root.removechild(college); } else if ("c3".equals(collegename)) { element newchild = doc.createelement("class"); newchild.setattribute("name", "c4"); college.appendchild(newchild); } } } } // 新增节点 element addcollege = doc.createelement("college"); addcollege.setattribute("name", "c5"); root.appendchild(addcollege); text text = doc.createtextnode("text"); addcollege.appendchild(text); // 将修改后的文档保存到文件 transformerfactory transfactory = transformerfactory.newinstance(); transformer transformer = transfactory.newtransformer(); domsource domsource = new domsource(doc); file file = new file("src/dom-modify.xml"); if (file.exists()) { file.delete(); } file.createnewfile(); fileoutputstream out = new fileoutputstream(file); streamresult xmlresult = new streamresult(out); transformer.transform(domsource, xmlresult); system.out.println(file.getabsolutepath()); } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } catch (transformerconfigurationexception e) { e.printstacktrace(); } catch (transformerexception e) { e.printstacktrace(); } } }
该代码只要稍做修改,即可变得更加简洁,无需一直写if来判断是否有子节点。
2、【sax (simple api for xml) 】
sax不用将整个文档加载到内存,基于事件驱动的api(observer模式),用户只需要注册自己感兴趣的事件即可。sax提供entityresolver, dtdhandler, contenthandler, errorhandler接口,分别用于监听解析实体事件、dtd处理事件、正文处理事件和处理出错事件,与awt类似,sax还提供了一个对这4个接口默认的类defaulthandler(这里的默认实现,其实就是一个空方法),一般只要继承defaulthandler,重写自己感兴趣的事件即可。
示例代码:
package test.xml; import java.io.ioexception; import java.io.inputstream; import javax.xml.parsers.parserconfigurationexception; import javax.xml.parsers.saxparser; import javax.xml.parsers.saxparserfactory; import org.xml.sax.attributes; import org.xml.sax.inputsource; import org.xml.sax.locator; import org.xml.sax.saxexception; import org.xml.sax.saxparseexception; import org.xml.sax.helpers.defaulthandler; /** * * @author whwang */ public class testsax { public static void main(string[] args) { read(); write(); } public static void read() { try { saxparserfactory factory = saxparserfactory.newinstance(); saxparser parser = factory.newsaxparser(); inputstream in = testsax.class.getclassloader().getresourceasstream("test.xml"); parser.parse(in, new myhandler()); } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } public static void write() { system.err.println("纯sax对于写操作无能为力"); } } // 重写对自己感兴趣的事件处理方法 class myhandler extends defaulthandler { @override public inputsource resolveentity(string publicid, string systemid) throws ioexception, saxexception { return super.resolveentity(publicid, systemid); } @override public void notationdecl(string name, string publicid, string systemid) throws saxexception { super.notationdecl(name, publicid, systemid); } @override public void unparsedentitydecl(string name, string publicid, string systemid, string notationname) throws saxexception { super.unparsedentitydecl(name, publicid, systemid, notationname); } @override public void setdocumentlocator(locator locator) { super.setdocumentlocator(locator); } @override public void startdocument() throws saxexception { system.err.println("开始解析文档"); } @override public void enddocument() throws saxexception { system.err.println("解析结束"); } @override public void startprefixmapping(string prefix, string uri) throws saxexception { super.startprefixmapping(prefix, uri); } @override public void endprefixmapping(string prefix) throws saxexception { super.endprefixmapping(prefix); } @override public void startelement(string uri, string localname, string qname, attributes attributes) throws saxexception { system.err.print("element: " + qname + ", attr: "); print(attributes); } @override public void endelement(string uri, string localname, string qname) throws saxexception { super.endelement(uri, localname, qname); } @override public void characters(char[] ch, int start, int length) throws saxexception { super.characters(ch, start, length); } @override public void ignorablewhitespace(char[] ch, int start, int length) throws saxexception { super.ignorablewhitespace(ch, start, length); } @override public void processinginstruction(string target, string data) throws saxexception { super.processinginstruction(target, data); } @override public void skippedentity(string name) throws saxexception { super.skippedentity(name); } @override public void warning(saxparseexception e) throws saxexception { super.warning(e); } @override public void error(saxparseexception e) throws saxexception { super.error(e); } @override public void fatalerror(saxparseexception e) throws saxexception { super.fatalerror(e); } private void print(attributes attrs) { if (attrs == null) return; system.err.print("["); for (int i = 0; i < attrs.getlength(); i++) { system.err.print(attrs.getqname(i) + " = " + attrs.getvalue(i)); if (i != attrs.getlength() - 1) { system.err.print(", "); } } system.err.println("]"); } }
3、【jdom】
jdom与dom非常类似,它是处理xml的纯java api,api大量使用了collections类,且jdom仅使用具体类而不使用接口。 jdom 它自身不包含解析器。它通常使用 sax2 解析器来解析和验证输入 xml 文档(尽管它还可以将以前构造的 dom 表示作为输入)。它包含一些转换器以将 jdom 表示输出成 sax2 事件流、dom 模型或 xml 文本文档
示例代码:
package test.xml; import java.io.file; import java.io.fileoutputstream; import java.io.ioexception; import java.io.inputstream; import java.util.list; import org.jdom.attribute; import org.jdom.document; import org.jdom.element; import org.jdom.jdomexception; import org.jdom.input.saxbuilder; import org.jdom.output.xmloutputter; /** * jdom读写xml * @author whwang */ public class testjdom { public static void main(string[] args) { //read(); write(); } public static void read() { try { boolean validate = false; saxbuilder builder = new saxbuilder(validate); inputstream in = testjdom.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.build(in); // 获取根节点 <university> element root = doc.getrootelement(); readnode(root, ""); } catch (jdomexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } @suppresswarnings("unchecked") public static void readnode(element root, string prefix) { if (root == null) return; // 获取属性 list<attribute> attrs = root.getattributes(); if (attrs != null && attrs.size() > 0) { system.err.print(prefix); for (attribute attr : attrs) { system.err.print(attr.getvalue() + " "); } system.err.println(); } // 获取他的子节点 list<element> childnodes = root.getchildren(); prefix += "\t"; for (element e : childnodes) { readnode(e, prefix); } } public static void write() { boolean validate = false; try { saxbuilder builder = new saxbuilder(validate); inputstream in = testjdom.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.build(in); // 获取根节点 <university> element root = doc.getrootelement(); // 修改属性 root.setattribute("name", "tsu"); // 删除 boolean isremoved = root.removechildren("college"); system.err.println(isremoved); // 新增 element newcollege = new element("college"); newcollege.setattribute("name", "new_college"); element newclass = new element("class"); newclass.setattribute("name", "ccccc"); newcollege.addcontent(newclass); root.addcontent(newcollege); xmloutputter out = new xmloutputter(); file file = new file("src/jdom-modify.xml"); if (file.exists()) { file.delete(); } file.createnewfile(); fileoutputstream fos = new fileoutputstream(file); out.output(doc, fos); } catch (jdomexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
4、【dom4j】
dom4j是目前在xml解析方面是最优秀的(hibernate、sun的jaxm也都使用dom4j来解析xml),它合并了许多超出基本 xml 文档表示的功能,包括集成的 xpath 支持、xml schema 支持以及用于大文档或流化文档的基于事件的处理
示例代码:
package test.xml; import java.io.file; import java.io.filewriter; import java.io.ioexception; import java.io.inputstream; import java.util.list; import org.dom4j.attribute; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.documenthelper; import org.dom4j.element; import org.dom4j.processinginstruction; import org.dom4j.visitorsupport; import org.dom4j.io.saxreader; import org.dom4j.io.xmlwriter; /** * dom4j读写xml * @author whwang */ public class testdom4j { public static void main(string[] args) { read1(); //read2(); //write(); } public static void read1() { try { saxreader reader = new saxreader(); inputstream in = testdom4j.class.getclassloader().getresourceasstream("test.xml"); document doc = reader.read(in); element root = doc.getrootelement(); readnode(root, ""); } catch (documentexception e) { e.printstacktrace(); } } @suppresswarnings("unchecked") public static void readnode(element root, string prefix) { if (root == null) return; // 获取属性 list<attribute> attrs = root.attributes(); if (attrs != null && attrs.size() > 0) { system.err.print(prefix); for (attribute attr : attrs) { system.err.print(attr.getvalue() + " "); } system.err.println(); } // 获取他的子节点 list<element> childnodes = root.elements(); prefix += "\t"; for (element e : childnodes) { readnode(e, prefix); } } public static void read2() { try { saxreader reader = new saxreader(); inputstream in = testdom4j.class.getclassloader().getresourceasstream("test.xml"); document doc = reader.read(in); doc.accept(new myvistor()); } catch (documentexception e) { e.printstacktrace(); } } public static void write() { try { // 创建一个xml文档 document doc = documenthelper.createdocument(); element university = doc.addelement("university"); university.addattribute("name", "tsu"); // 注释 university.addcomment("这个是根节点"); element college = university.addelement("college"); college.addattribute("name", "cccccc"); college.settext("text"); file file = new file("src/dom4j-modify.xml"); if (file.exists()) { file.delete(); } file.createnewfile(); xmlwriter out = new xmlwriter(new filewriter(file)); out.write(doc); out.flush(); out.close(); } catch (ioexception e) { e.printstacktrace(); } } } class myvistor extends visitorsupport { public void visit(attribute node) { system.out.println("attibute: " + node.getname() + "=" + node.getvalue()); } public void visit(element node) { if (node.istextonly()) { system.out.println("element: " + node.getname() + "=" + node.gettext()); } else { system.out.println(node.getname()); } } @override public void visit(processinginstruction node) { system.out.println("pi:" + node.gettarget() + " " + node.gettext()); } }
三、【性能测试】
环境:amd4400+ 2.0+ghz主频 jdk6.0
运行参数:-xms400m -xmx400m
xml文件大小:10.7m
结果:
dom: >581297ms
sax: 8829ms
jdom: 581297ms
dom4j: 5309ms
时间包括io的,只是进行了简单的测试,仅供参考!!!!
四、【对比】
1、【dom】
dom是基于树的结构,通常需要加载整文档和构造dom树,然后才能开始工作。
优点:
a、由于整棵树在内存中,因此可以对xml文档随机访问
b、可以对xml文档进行修改操作
c、较sax,dom使用也更简单。
缺点:
a、整个文档必须一次性解析完
a、由于整个文档都需要载入内存,对于大文档成本高
2、【sax】
sax类似流媒体,它基于事件驱动的,因此无需将整个文档载入内存,使用者只需要监听自己感兴趣的事件即可。
优点:
a、无需将整个xml文档载入内存,因此消耗内存少
b、可以注册多个contenthandler
缺点:
a、不能随机的访问xml中的节点
b、不能修改文档
3、【jdom】
jdom是纯java的处理xml的api,其api中大量使用collections类,
优点:
a、dom方式的优点
b、具有sax的java规则
缺点
a、dom方式的缺点
4、【dom4j】
这4中xml解析方式中,最优秀的一个,集易用和性能于一身。
五、【小插曲xpath】
xpath 是一门在 xml 文档中查找信息的语言, 可用来在 xml 文档中对元素和属性进行遍历。xpath 是 w3c xslt 标准的主要元素,并且 xquery 和 xpointer 同时被构建于 xpath 表达之上。因此,对 xpath 的理解是很多高级 xml 应用的基础。
xpath非常类似对数据库操作的sql语言,或者说jquery,它可以方便开发者抓起文档中需要的东西。(dom4j也支持xpath)
示例代码:
package test.xml; import java.io.ioexception; import java.io.inputstream; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import javax.xml.xpath.xpath; import javax.xml.xpath.xpathconstants; import javax.xml.xpath.xpathexpression; import javax.xml.xpath.xpathexpressionexception; import javax.xml.xpath.xpathfactory; import org.w3c.dom.document; import org.w3c.dom.nodelist; import org.xml.sax.saxexception; public class testxpath { public static void main(string[] args) { read(); } public static void read() { try { documentbuilderfactory dbf = documentbuilderfactory.newinstance(); documentbuilder builder = dbf.newdocumentbuilder(); inputstream in = testxpath.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.parse(in); xpathfactory factory = xpathfactory.newinstance(); xpath xpath = factory.newxpath(); // 选取所有class元素的name属性 // xpath语法介绍: http://w3school.com.cn/xpath/ xpathexpression expr = xpath.compile("//class/@name"); nodelist nodes = (nodelist) expr.evaluate(doc, xpathconstants.nodeset); for (int i = 0; i < nodes.getlength(); i++) { system.out.println("name = " + nodes.item(i).getnodevalue()); } } catch (xpathexpressionexception e) { e.printstacktrace(); } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
六、【补充】
注意4种解析方法对textnode(文本节点)的处理:
1、在使用dom时,调用node.getchildnodes()获取该节点的子节点,文本节点也会被当作一个node来返回,如:
<?xml version="1.0" encoding="utf-8"?> <university name="pku"> <college name="c1"> <class name="class1"> <student name="stu1" sex='male' age="21" /> <student name="stu2" sex='female' age="20" /> <student name="stu3" sex='female' age="20" /> </class> </college> </university>
package test.xml; import java.io.filenotfoundexception; import java.io.ioexception; import java.io.inputstream; import java.util.arrays; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import org.w3c.dom.document; import org.w3c.dom.element; import org.w3c.dom.node; import org.w3c.dom.nodelist; import org.xml.sax.saxexception; /** * dom读写xml * @author whwang */ public class testdom2 { public static void main(string[] args) { read(); } public static void read() { documentbuilderfactory dbf = documentbuilderfactory.newinstance(); try { documentbuilder builder = dbf.newdocumentbuilder(); inputstream in = testdom2.class.getclassloader().getresourceasstream("test.xml"); document doc = builder.parse(in); // root <university> element root = doc.getdocumentelement(); if (root == null) return; // system.err.println(root.getattribute("name")); // all college node nodelist collegenodes = root.getchildnodes(); if (collegenodes == null) return; system.err.println("university子节点数:" + collegenodes.getlength()); system.err.println("子节点如下:"); for(int i = 0; i < collegenodes.getlength(); i++) { node college = collegenodes.item(i); if (college == null) continue; if (college.getnodetype() == node.element_node) { system.err.println("\t元素节点:" + college.getnodename()); } else if (college.getnodetype() == node.text_node) { system.err.println("\t文本节点:" + arrays.tostring(college.gettextcontent().getbytes())); } } } catch (parserconfigurationexception e) { e.printstacktrace(); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (saxexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }
输出的结果是:
university子节点数:3 子节点如下: 文本节点:[10, 9] 元素节点:college 文本节点:[10]
其中\n的ascii码为10,\t的ascii码为9。结果让人大吃一惊,university的子节点数不是1,也不是2,而是3,这3个子节点都是谁呢?为了看得更清楚点,把xml文档改为:
<?xml version="1.0" encoding="utf-8"?> <university name="pku">11 <college name="c1"> <class name="class1"> <student name="stu1" sex='male' age="21" /> <student name="stu2" sex='female' age="20" /> <student name="stu3" sex='female' age="20" /> </class> </college>22 </university>
还是上面的程序,输出结果为:
university子节点数:3 子节点如下: 文本节点:[49, 49, 10, 9] 元素节点:college 文本节点:[50, 50, 10]
其中数字1的ascii码为49,数字2的ascii码为50。
2、使用sax来解析同dom,当你重写它的public void characters(char[] ch, int start, int length)方法时,你就能看到。
3、jdom,调用node.getchildren()只返回子节点,不包括textnode节点(不管该节点是否有text信息)。如果要获取该节点的text信息,可以调用node.gettext()方法,该方法返回节点的text信息,也包括\n\t等特殊字符。
4、dom4j同jdom
推荐阅读
-
Java线程休眠_动力节点Java学院整理
-
Java线程让步_动力节点Java学院整理
-
Java Thread中start()和run()的区别_动力节点Java学院整理
-
Sax解析xml_动力节点Java学院整理
-
Ehcache简介_动力节点Java学院整理
-
Java class文件格式之特殊字符串_动力节点Java学院整理
-
HttpServletRequest对象常用功能_动力节点Java学院整理
-
HttpServletResponse乱码问题_动力节点Java学院整理
-
Java过滤器filter_动力节点Java学院整理
-
HttpServletRequest对象简介_动力节点Java学院整理