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); //rootElementroot=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;i3、【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; //获取属性 List attrs=root.getAttributes(); if(attrs!=null&&attrs.size()>0){ System.err.print(prefix); for(Attributeattr:attrs){ System.err.print(attr.getValue()+""); } System.err.println(); } //获取他的子节点 List childNodes=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(); } //获取他的子节点 List childNodes=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); //rootElementroot=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