关键字:java解析xml、解析xml四种方法、dom、sax、jdom、dom4j、xpath【引言】目前在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
以上就是详细介绍解析xml四种方法的示例代码的详细内容。
