JAVA通过XPath解析XML性能比较详解
最近在做一个小项目,使用到xml文件解析技术,通过对该技术的了解和使用,总结了以下内容。
1 xml文件解析的4种方法
通常解析xml文件有四种经典的方法。基本的解析方式有两种,一种叫sax,另一种叫dom。sax是基于事件流的解析,dom是基于xml文档树结构的解析。在此基础上,为了减少dom、sax的编码量,出现了jdom,其优点是,20-80原则(帕累托法则),极大减少了代码量。通常情况下jdom使用时满足要实现的功能简单,如解析、创建等要求。但在底层,jdom还是使用sax(最常用)、dom、xanan文档。另外一种是dom4j,是一个非常非常优秀的java xml api,具有性能优异、功能强大和极端易用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的 java 软件都在使用 dom4j 来读写 xml,特别值得一提的是连 sun 的 jaxm 也在用 dom4j。
2 xpath简单介绍
xpath 是一门在 xml 文档中查找信息的语言。xpath 用于在 xml 文档中通过元素和属性进行导航,并对元素和属性进行遍历。xpath 是 w3c xslt 标准的主要元素,并且 xquery 和 xpointer 同时被构建于 xpath 表达之上。因此,对 xpath 的理解是很多高级 xml 应用的基础。xpath非常类似对数据库操作的sql语言,或者说jquery,它可以方便开发者抓起文档中需要的东西。其中dom4j也支持xpath的使用。
3 dom4j使用xpath
dom4j使用xpath解析xml文档是,首先需要在项目中引用两个jar包:
dom4j-1.6.1.jar:dom4j软件包,下载地址;
jaxen-xx.xx.jar:通常不添加此包,会引发异常(java.lang.noclassdeffounderror: org/jaxen/jaxenexception),下载地址。
3.1 命名空间(namespace)的干扰
在处理由excel文件或其他格式文件转换的xml文件时,通常会遇到通过xpath解析得不到结果的情况。这种情况通常是由于命名空间的存在导致的。以下述内容的xml文件为例,通过xpath=" // workbook/ worksheet / table / row[1]/ cell[1]/data[1] "进行简单的检索,通常是没有结果出现的。这就是由于命名空间namespace(xmlns="urn:schemas-microsoft-com:office:spreadsheet")导致的。
<workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/tr/rec-html40"> <worksheet ss:name="sheet1"> <table ss:expandedcolumncount="81" ss:expandedrowcount="687" x:fullcolumns="1" x:fullrows="1" ss:defaultcolumnwidth="52.5" ss:defaultrowheight="15.5625"> <row ss:autofitheight="0"> <cell> <data ss:type="string">敲代码的耗子</data> </cell> </row> <row ss:autofitheight="0"> <cell> <data ss:type="string">sunny</data> </cell> </row> </table> </worksheet> </workbook>
3.2 xpath对带有命名空间的xml文件解析
第一种方法(read1()函数):使用xpath语法中自带的local-name() 和 namespace-uri() 指定你要使用的节点名和命名空间。 xpath表达式书写较为麻烦。
第二种方法(read2()函数):设置xpath的命名空间,利用setnamespaceuris()函数。
第三种方法(read3()函数):设置documentfactory()的命名空间 ,使用的函数是setxpathnamespaceuris()。二和三两种方法的xpath表达式书写相对简单。
第四种方法(read4()函数):方法和第三种一样,但是xpath表达式不同(程序具体体现),主要是为了检验xpath表达式的不同,主要指完整程度,是否会对检索效率产生影响。(以上四种方法均通过dom4j结合xpath对xml文件进行解析)
第五种方法(read5()函数):使用dom结合xpath对xml文件进行解析,主要是为了检验性能差异。
没有什么能够比代码更能说明问题的了!果断上代码!
package xpath; import java.io.ioexception; import java.io.inputstream; import java.util.hashmap; import java.util.list; import java.util.map; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import javax.xml.parsers.parserconfigurationexception; import javax.xml.xpath.xpathconstants; import javax.xml.xpath.xpathexpression; import javax.xml.xpath.xpathexpressionexception; import javax.xml.xpath.xpathfactory; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.element; import org.dom4j.xpath; import org.dom4j.io.saxreader; import org.w3c.dom.nodelist; import org.xml.sax.saxexception; /** * dom4j dom xml xpath * @author hao */ public class testdom4jxpath { public static void main(string[] args) { read1(); read2(); read3(); read4();//read3()方法一样,但是xpath表达式不同 read5(); } public static void read1() { /* * use local-name() and namespace-uri() in xpath */ try { long starttime=system.currenttimemillis(); saxreader reader = new saxreader(); inputstream in = testdom4jxpath.class.getclassloader().getresourceasstream("xpath\\xxx.xml"); document doc = reader.read(in); /*string xpath ="//*[local-name()='workbook' and namespace-uri()='urn:schemas-microsoft-com:office:spreadsheet']" + "/*[local-name()='worksheet']" + "/*[local-name()='table']" + "/*[local-name()='row'][4]" + "/*[local-name()='cell'][3]" + "/*[local-name()='data'][1]";*/ string xpath ="//*[local-name()='row'][4]/*[local-name()='cell'][3]/*[local-name()='data'][1]"; system.err.println("=====use local-name() and namespace-uri() in xpath===="); system.err.println("xpath:" + xpath); @suppresswarnings("unchecked") list<element> list = doc.selectnodes(xpath); for(object o:list){ element e = (element) o; string show=e.getstringvalue(); system.out.println("show = " + show); long endtime=system.currenttimemillis(); system.out.println("程序运行时间: "+(endtime-starttime)+"ms"); } } catch (documentexception e) { e.printstacktrace(); } } public static void read2() { /* * set xpath namespace(setnamespaceuris) */ try { long starttime=system.currenttimemillis(); map map = new hashmap(); map.put("workbook","urn:schemas-microsoft-com:office:spreadsheet"); saxreader reader = new saxreader(); inputstream in = testdom4jxpath.class.getclassloader().getresourceasstream("xpath\\xxx.xml"); document doc = reader.read(in); string xpath ="//workbook:row[4]/workbook:cell[3]/workbook:data[1]"; system.err.println("=====use setnamespaceuris() to set xpath namespace===="); system.err.println("xpath:" + xpath); xpath x = doc.createxpath(xpath); x.setnamespaceuris(map); @suppresswarnings("unchecked") list<element> list = x.selectnodes(doc); for(object o:list){ element e = (element) o; string show=e.getstringvalue(); system.out.println("show = " + show); long endtime=system.currenttimemillis(); system.out.println("程序运行时间: "+(endtime-starttime)+"ms"); } } catch (documentexception e) { e.printstacktrace(); } } public static void read3() { /* * set documentfactory() namespace(setxpathnamespaceuris) */ try { long starttime=system.currenttimemillis(); map map = new hashmap(); map.put("workbook","urn:schemas-microsoft-com:office:spreadsheet"); saxreader reader = new saxreader(); inputstream in = testdom4jxpath.class.getclassloader().getresourceasstream("xpath\\xxx.xml"); reader.getdocumentfactory().setxpathnamespaceuris(map); document doc = reader.read(in); string xpath ="//workbook:row[4]/workbook:cell[3]/workbook:data[1]"; system.err.println("=====use setxpathnamespaceuris() to set documentfactory() namespace===="); system.err.println("xpath:" + xpath); @suppresswarnings("unchecked") list<element> list = doc.selectnodes(xpath); for(object o:list){ element e = (element) o; string show=e.getstringvalue(); system.out.println("show = " + show); long endtime=system.currenttimemillis(); system.out.println("程序运行时间: "+(endtime-starttime)+"ms"); } } catch (documentexception e) { e.printstacktrace(); } } public static void read4() { /* * 同read3()方法一样,但是xpath表达式不同 */ try { long starttime=system.currenttimemillis(); map map = new hashmap(); map.put("workbook","urn:schemas-microsoft-com:office:spreadsheet"); saxreader reader = new saxreader(); inputstream in = testdom4jxpath.class.getclassloader().getresourceasstream("xpath\\xxx.xml"); reader.getdocumentfactory().setxpathnamespaceuris(map); document doc = reader.read(in); string xpath ="//workbook:worksheet/workbook:table/workbook:row[4]/workbook:cell[3]/workbook:data[1]"; system.err.println("=====use setxpathnamespaceuris() to set documentfactory() namespace===="); system.err.println("xpath:" + xpath); @suppresswarnings("unchecked") list<element> list = doc.selectnodes(xpath); for(object o:list){ element e = (element) o; string show=e.getstringvalue(); system.out.println("show = " + show); long endtime=system.currenttimemillis(); system.out.println("程序运行时间: "+(endtime-starttime)+"ms"); } } catch (documentexception e) { e.printstacktrace(); } } public static void read5() { /* * dom and xpath */ try { long starttime=system.currenttimemillis(); documentbuilderfactory dbf = documentbuilderfactory.newinstance(); dbf.setnamespaceaware(false); documentbuilder builder = dbf.newdocumentbuilder(); inputstream in = testdom4jxpath.class.getclassloader().getresourceasstream("xpath\\xxx.xml"); org.w3c.dom.document doc = builder.parse(in); xpathfactory factory = xpathfactory.newinstance(); javax.xml.xpath.xpath x = factory.newxpath(); //选取所有class元素的name属性 string xpath = "//workbook/worksheet/table/row[4]/cell[3]/data[1]"; system.err.println("=====dom xpath===="); system.err.println("xpath:" + xpath); xpathexpression expr = x.compile(xpath); nodelist nodes = (nodelist)expr.evaluate(doc, xpathconstants.node); for(int i = 0; i<nodes.getlength();i++) { system.out.println("show = " + nodes.item(i).getnodevalue()); long endtime=system.currenttimemillis(); system.out.println("程序运行时间: "+(endtime-starttime)+"ms"); } } catch(xpathexpressionexception e) { e.printstacktrace(); } catch(parserconfigurationexception e) { e.printstacktrace(); } catch(saxexception e) { e.printstacktrace(); } catch(ioexception e) { e.printstacktrace(); } } }
3.3 不同方法的性能比较
为了比较几种方法的解析性能,实验过程中使用了6m以上大小,7万行以上的xml文件(xxx.xml)进行10轮测试,如下所述:
图1 xpath使用性能对比
方法名称 |
平均运行时间 |
xpath表达式 |
read1() |
1663ms |
//*[local-name()='row'][4]/*[local-name()='cell'][3]/*[local-name()='data'][1] |
read2() |
2184ms |
//workbook:row[4]/workbook:cell[3]/workbook:data[1] |
read3() |
601ms |
//workbook:row[4]/workbook:cell[3]/workbook:data[1] |
read4() |
472ms |
//workbook:worksheet/workbook:table/workbook:row[4]/workbook:cell[3]/workbook:data[1] |
read5() |
1094ms |
//workbook/worksheet/table/row[4]/cell[3]/data[1] |
表1 平均性能统计
由以上性能对比可知:
1、read4()方法运行时间最短,即运用dom4j方法调用全路径(从根节点出发)xpath表达式解析xml文件耗时最短;
2、运用dom解析方法所使用的xpath表达式最为简单(可以写作//row[4]/cell[3]/data[1]),因dom中可以通过setnamespaceaware(false)方法使命名空间失效。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。