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(DocumentObjectModel)】
由W3C提供的接口,它将整个XML文档读入内存,构建一个DOM树来对各个节点(Node)进行操作。
示例代码:
后文代码中有使用到text.xml(该文档放在src路径下,既编译后在classes路径下),都是指该xml文档。
packagetest.xml;
importjava.io.File;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importjavax.xml.parsers.ParserConfigurationException;
importjavax.xml.transform.Transformer;
importjavax.xml.transform.TransformerConfigurationException;
importjavax.xml.transform.TransformerException;
importjavax.xml.transform.TransformerFactory;
importjavax.xml.transform.dom.DOMSource;
importjavax.xml.transform.stream.StreamResult;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
importorg.w3c.dom.Node;
importorg.w3c.dom.NodeList;
importorg.w3c.dom.Text;
importorg.xml.sax.SAXException;
/**
*dom读写xml
*@authorwhwang
*/
publicclassTestDom{
publicstaticvoidmain(String[]args){
read();
//write();
}
publicstaticvoidread(){
DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance();
try{
DocumentBuilderbuilder=dbf.newDocumentBuilder();
InputStreamin=TestDom.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=builder.parse(in);
//root
Elementroot=doc.getDocumentElement();
if(root==null)return;
System.err.println(root.getAttribute("name"));
//allcollegenode
NodeListcollegeNodes=root.getChildNodes();
if(collegeNodes==null)return;
for(inti=0;i
Elementroot=doc.getDocumentElement();
if(root==null)return;
//修改属性
root.setAttribute("name","tsu");
NodeListcollegeNodes=root.getChildNodes();
if(collegeNodes!=null){
for(inti=0;i
该代码只要稍做修改,即可变得更加简洁,无需一直写if来判断是否有子节点。
2、【SAX(SimpleAPIforXML)】
SAX不用将整个文档加载到内存,基于事件驱动的API(Observer模式),用户只需要注册自己感兴趣的事件即可。SAX提供EntityResolver,DTDHandler,ContentHandler,ErrorHandler接口,分别用于监听解析实体事件、DTD处理事件、正文处理事件和处理出错事件,与AWT类似,SAX还提供了一个对这4个接口默认的类DefaultHandler(这里的默认实现,其实就是一个空方法),一般只要继承DefaultHandler,重写自己感兴趣的事件即可。
示例代码:
packagetest.xml;
importjava.io.IOException;
importjava.io.InputStream;
importjavax.xml.parsers.ParserConfigurationException;
importjavax.xml.parsers.SAXParser;
importjavax.xml.parsers.SAXParserFactory;
importorg.xml.sax.Attributes;
importorg.xml.sax.InputSource;
importorg.xml.sax.Locator;
importorg.xml.sax.SAXException;
importorg.xml.sax.SAXParseException;
importorg.xml.sax.helpers.DefaultHandler;
/**
*
*@authorwhwang
*/
publicclassTestSAX{
publicstaticvoidmain(String[]args){
read();
write();
}
publicstaticvoidread(){
try{
SAXParserFactoryfactory=SAXParserFactory.newInstance();
SAXParserparser=factory.newSAXParser();
InputStreamin=TestSAX.class.getClassLoader().getResourceAsStream("test.xml");
parser.parse(in,newMyHandler());
}catch(ParserConfigurationExceptione){
e.printStackTrace();
}catch(SAXExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
publicstaticvoidwrite(){
System.err.println("纯SAX对于写操作无能为力");
}
}
//重写对自己感兴趣的事件处理方法
classMyHandlerextendsDefaultHandler{
@Override
publicInputSourceresolveEntity(StringpublicId,StringsystemId)
throwsIOException,SAXException{
returnsuper.resolveEntity(publicId,systemId);
}
@Override
publicvoidnotationDecl(Stringname,StringpublicId,StringsystemId)
throwsSAXException{
super.notationDecl(name,publicId,systemId);
}
@Override
publicvoidunparsedEntityDecl(Stringname,StringpublicId,
StringsystemId,StringnotationName)throwsSAXException{
super.unparsedEntityDecl(name,publicId,systemId,notationName);
}
@Override
publicvoidsetDocumentLocator(Locatorlocator){
super.setDocumentLocator(locator);
}
@Override
publicvoidstartDocument()throwsSAXException{
System.err.println("开始解析文档");
}
@Override
publicvoidendDocument()throwsSAXException{
System.err.println("解析结束");
}
@Override
publicvoidstartPrefixMapping(Stringprefix,Stringuri)
throwsSAXException{
super.startPrefixMapping(prefix,uri);
}
@Override
publicvoidendPrefixMapping(Stringprefix)throwsSAXException{
super.endPrefixMapping(prefix);
}
@Override
publicvoidstartElement(Stringuri,StringlocalName,StringqName,
Attributesattributes)throwsSAXException{
System.err.print("Element:"+qName+",attr:");
print(attributes);
}
@Override
publicvoidendElement(Stringuri,StringlocalName,StringqName)
throwsSAXException{
super.endElement(uri,localName,qName);
}
@Override
publicvoidcharacters(char[]ch,intstart,intlength)
throwsSAXException{
super.characters(ch,start,length);
}
@Override
publicvoidignorableWhitespace(char[]ch,intstart,intlength)
throwsSAXException{
super.ignorableWhitespace(ch,start,length);
}
@Override
publicvoidprocessingInstruction(Stringtarget,Stringdata)
throwsSAXException{
super.processingInstruction(target,data);
}
@Override
publicvoidskippedEntity(Stringname)throwsSAXException{
super.skippedEntity(name);
}
@Override
publicvoidwarning(SAXParseExceptione)throwsSAXException{
super.warning(e);
}
@Override
publicvoiderror(SAXParseExceptione)throwsSAXException{
super.error(e);
}
@Override
publicvoidfatalError(SAXParseExceptione)throwsSAXException{
super.fatalError(e);
}
privatevoidprint(Attributesattrs){
if(attrs==null)return;
System.err.print("[");
for(inti=0;i
3、【JDOM】
JDOM与DOM非常类似,它是处理XML的纯JAVAAPI,API大量使用了Collections类,且JDOM仅使用具体类而不使用接口。JDOM它自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档
示例代码:
packagetest.xml;
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.util.List;
importorg.jdom.Attribute;
importorg.jdom.Document;
importorg.jdom.Element;
importorg.jdom.JDOMException;
importorg.jdom.input.SAXBuilder;
importorg.jdom.output.XMLOutputter;
/**
*JDom读写xml
*@authorwhwang
*/
publicclassTestJDom{
publicstaticvoidmain(String[]args){
//read();
write();
}
publicstaticvoidread(){
try{
booleanvalidate=false;
SAXBuilderbuilder=newSAXBuilder(validate);
InputStreamin=TestJDom.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=builder.build(in);
//获取根节点
Elementroot=doc.getRootElement();
readNode(root,"");
}catch(JDOMExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
publicstaticvoidreadNode(Elementroot,Stringprefix){
if(root==null)return;
//获取属性
Listattrs=root.getAttributes();
if(attrs!=null&&attrs.size()>0){
System.err.print(prefix);
for(Attributeattr:attrs){
System.err.print(attr.getValue()+"");
}
System.err.println();
}
//获取他的子节点
ListchildNodes=root.getChildren();
prefix+="\t";
for(Elemente:childNodes){
readNode(e,prefix);
}
}
publicstaticvoidwrite(){
booleanvalidate=false;
try{
SAXBuilderbuilder=newSAXBuilder(validate);
InputStreamin=TestJDom.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=builder.build(in);
//获取根节点
Elementroot=doc.getRootElement();
//修改属性
root.setAttribute("name","tsu");
//删除
booleanisRemoved=root.removeChildren("college");
System.err.println(isRemoved);
//新增
ElementnewCollege=newElement("college");
newCollege.setAttribute("name","new_college");
ElementnewClass=newElement("class");
newClass.setAttribute("name","ccccc");
newCollege.addContent(newClass);
root.addContent(newCollege);
XMLOutputterout=newXMLOutputter();
Filefile=newFile("src/jdom-modify.xml");
if(file.exists()){
file.delete();
}
file.createNewFile();
FileOutputStreamfos=newFileOutputStream(file);
out.output(doc,fos);
}catch(JDOMExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
4、【DOM4j】
dom4j是目前在xml解析方面是最优秀的(Hibernate、Sun的JAXM也都使用dom4j来解析XML),它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XMLSchema支持以及用于大文档或流化文档的基于事件的处理
示例代码:
packagetest.xml;
importjava.io.File;
importjava.io.FileWriter;
importjava.io.IOException;
importjava.io.InputStream;
importjava.util.List;
importorg.dom4j.Attribute;
importorg.dom4j.Document;
importorg.dom4j.DocumentException;
importorg.dom4j.DocumentHelper;
importorg.dom4j.Element;
importorg.dom4j.ProcessingInstruction;
importorg.dom4j.VisitorSupport;
importorg.dom4j.io.SAXReader;
importorg.dom4j.io.XMLWriter;
/**
*Dom4j读写xml
*@authorwhwang
*/
publicclassTestDom4j{
publicstaticvoidmain(String[]args){
read1();
//read2();
//write();
}
publicstaticvoidread1(){
try{
SAXReaderreader=newSAXReader();
InputStreamin=TestDom4j.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=reader.read(in);
Elementroot=doc.getRootElement();
readNode(root,"");
}catch(DocumentExceptione){
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
publicstaticvoidreadNode(Elementroot,Stringprefix){
if(root==null)return;
//获取属性
Listattrs=root.attributes();
if(attrs!=null&&attrs.size()>0){
System.err.print(prefix);
for(Attributeattr:attrs){
System.err.print(attr.getValue()+"");
}
System.err.println();
}
//获取他的子节点
ListchildNodes=root.elements();
prefix+="\t";
for(Elemente:childNodes){
readNode(e,prefix);
}
}
publicstaticvoidread2(){
try{
SAXReaderreader=newSAXReader();
InputStreamin=TestDom4j.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=reader.read(in);
doc.accept(newMyVistor());
}catch(DocumentExceptione){
e.printStackTrace();
}
}
publicstaticvoidwrite(){
try{
//创建一个xml文档
Documentdoc=DocumentHelper.createDocument();
Elementuniversity=doc.addElement("university");
university.addAttribute("name","tsu");
//注释
university.addComment("这个是根节点");
Elementcollege=university.addElement("college");
college.addAttribute("name","cccccc");
college.setText("text");
Filefile=newFile("src/dom4j-modify.xml");
if(file.exists()){
file.delete();
}
file.createNewFile();
XMLWriterout=newXMLWriter(newFileWriter(file));
out.write(doc);
out.flush();
out.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classMyVistorextendsVisitorSupport{
publicvoidvisit(Attributenode){
System.out.println("Attibute:"+node.getName()+"="
+node.getValue());
}
publicvoidvisit(Elementnode){
if(node.isTextOnly()){
System.out.println("Element:"+node.getName()+"="
+node.getText());
}else{
System.out.println(node.getName());
}
}
@Override
publicvoidvisit(ProcessingInstructionnode){
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是W3CXSLT标准的主要元素,并且XQuery和XPointer同时被构建于XPath表达之上。因此,对XPath的理解是很多高级XML应用的基础。
XPath非常类似对数据库操作的SQL语言,或者说JQuery,它可以方便开发者抓起文档中需要的东西。(dom4j也支持xpath)
示例代码:
packagetest.xml;
importjava.io.IOException;
importjava.io.InputStream;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importjavax.xml.parsers.ParserConfigurationException;
importjavax.xml.xpath.XPath;
importjavax.xml.xpath.XPathConstants;
importjavax.xml.xpath.XPathExpression;
importjavax.xml.xpath.XPathExpressionException;
importjavax.xml.xpath.XPathFactory;
importorg.w3c.dom.Document;
importorg.w3c.dom.NodeList;
importorg.xml.sax.SAXException;
publicclassTestXPath{
publicstaticvoidmain(String[]args){
read();
}
publicstaticvoidread(){
try{
DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance();
DocumentBuilderbuilder=dbf.newDocumentBuilder();
InputStreamin=TestXPath.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=builder.parse(in);
XPathFactoryfactory=XPathFactory.newInstance();
XPathxpath=factory.newXPath();
//选取所有class元素的name属性
//XPath语法介绍:http://w3school.com.cn/xpath/
XPathExpressionexpr=xpath.compile("//class/@name");
NodeListnodes=(NodeList)expr.evaluate(doc,XPathConstants.NODESET);
for(inti=0;i
六、【补充】
注意4种解析方法对TextNode(文本节点)的处理:
1、在使用DOM时,调用node.getChildNodes()获取该节点的子节点,文本节点也会被当作一个Node来返回,如:
packagetest.xml;
importjava.io.FileNotFoundException;
importjava.io.IOException;
importjava.io.InputStream;
importjava.util.Arrays;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importjavax.xml.parsers.ParserConfigurationException;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
importorg.w3c.dom.Node;
importorg.w3c.dom.NodeList;
importorg.xml.sax.SAXException;
/**
*dom读写xml
*@authorwhwang
*/
publicclassTestDom2{
publicstaticvoidmain(String[]args){
read();
}
publicstaticvoidread(){
DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance();
try{
DocumentBuilderbuilder=dbf.newDocumentBuilder();
InputStreamin=TestDom2.class.getClassLoader().getResourceAsStream("test.xml");
Documentdoc=builder.parse(in);
//root
Elementroot=doc.getDocumentElement();
if(root==null)return;
//System.err.println(root.getAttribute("name"));
//allcollegenode
NodeListcollegeNodes=root.getChildNodes();
if(collegeNodes==null)return;
System.err.println("university子节点数:"+collegeNodes.getLength());
System.err.println("子节点如下:");
for(inti=0;i
输出的结果是:
university子节点数:3
子节点如下:
文本节点:[10,9]
元素节点:college
文本节点:[10]
其中\n的ASCII码为10,\t的ASCII码为9。结果让人大吃一惊,university的子节点数不是1,也不是2,而是3,这3个子节点都是谁呢?为了看得更清楚点,把xml文档改为:
11
22
还是上面的程序,输出结果为:
university子节点数:3 子节点如下: 文本节点:[49,49,10,9] 元素节点:college 文本节点:[50,50,10]
其中数字1的ASCII码为49,数字2的ASCII码为50。
2、使用SAX来解析同DOM,当你重写它的publicvoidcharacters(char[]ch,intstart,intlength)方法时,你就能看到。
3、JDOM,调用node.getChildren()只返回子节点,不包括TextNode节点(不管该节点是否有Text信息)。如果要获取该节点的Text信息,可以调用node.getText()方法,该方法返回节点的Text信息,也包括\n\t等特殊字符。
4、DOM4j同JDOM