[工作笔记] Dom4j 和 XPath 结合 选取 xml 中任意深度的(即所有的)特定元素

来源:互联网 发布:应对网络诈骗防范措施 编辑:程序博客网 时间:2024/06/03 18:49

第一次写技术相关的文章,技术含量不高,而且肯定会有诸多不足,欢迎大家提出意见,批评指正。

本文主要包含三部分:

  1. 为什么要写这篇工作笔记?
  2. 怎样解决的此问题?
  3. 总结

1 为什么要写这篇工作笔记?

这一段简单地说就是:工作中遇到问题,在网上找到遇到相同问题的同学们也没有解决问题,所以写了这篇文章备忘并提供一种此类问题的解决方案,跟大家交流学习。

用 Dom4j 解析 xml 文件是很简单的一件事,之前我也写过根据 xml 配置信息文件和应用部署地理位置设置应用配置文件(也是 xml)的程序。但是在使用其 XPath 解析 JasperReports 的模板文件(.jrxml)时却遇到了这样的问题:我需要解析出 jrxml 文件中任意深度的 <parameter> 元素(例如:<parameter name="Country" class="java.lang.String"/>),但无论我从根节点选取(如:/jasperReport/parameter)还是从当前节点选取所有的 <parameter>元素而不考虑其位置(如://jasperReport//parameter),得到的结果集 size 都是0。

不知道是不是我使用的 Dom4j 版本(1.6.1)中 XPath 比较特殊?不过我还是先去复习了 XPath 的语法等基础知识(可参考 w3school 的 XPath 教程:http://www.w3school.com.cn/xpath/xpath_syntax.asp),在程序中换了各种写法,仍然不行。在网上几大搜索引擎都搜索了一遍,看了十几个网页(有提问、技术文章、教程等),尝试了其中的各种方法,也没有解决问题,我看到的相关提问的回答中用过的方法我都用过了,均不奏效。

最后回到 Dom4j 本身,我仔细查看了一遍API,从其中可调用的方法中发现了可以让我查看其 XPath 格式的方法,打印出该 XPath 是从根节点选取的,把 “/” 修改成 “//” 就能选择任意深度的 <parameter> 元素了。下面说下具体过程,一方面给自己做个笔记,另一方面给前面说的那些提出问题的同学们提供一个解答,同时也弥补相关中文技术文章的空白。

2 怎样解决的此问题?

(跟解决的问题相比,解决问题的方式也许更有价值。)

2-1 调用 Dom4j 本身 Element 的 getPath() 方法查看其能理解的 XPath 格式

接着前面说的,下面我简要陈述一下本问题的关键解决过程。

打印出一个 Element 的 XPath 的代码如下:

1 Element parameterElement = (Element)parameterItr.next();2 System.out.println(parameterElement.getPath());

下图显示了控制台打印出的结果:

可见 Dom4j 的 XPath 跟 XPath 的语法是比较一致的,还是本人功课做得不够。知道了 Dom4j 理解的 XPath 格式,只要稍加修改,我的问题就解决了.

2-2 获取 jrxml 文件中任意深度的 <parameter> 元素

获取 jrxml 文件中任意深度的 <parameter> 元素的一个实例即是:获取该 xml 文件中的所有的 <parameter> 元素,同样对于其中所有的 <field> 元素也是可以的。根据上面 Dom4j 理解的 XPath 格式,要获取所有 <parameter> 元素的 XPath 应写为://*[name()='parameter'],类似的,要获取所有 <field> 元素的 XPath 应写为://*[name()='field']。

下面展示一个执行实例。要解析的 jrxml 源码为:

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?> 2 <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="ShipmentsReport" pageWidth="842" pageHeight="595" orientation="Landscape" columnWidth="812" leftMargin="15" rightMargin="15" topMargin="10" bottomMargin="10"> 3     <property name="net.sf.jasperreports.export.pdf.tagged" value="true"/> 4     <property name="net.sf.jasperreports.export.pdf.tag.language" value="EN-US"/> 5     <style name="Sans_Normal" isDefault="true" fontName="DejaVu Sans" fontSize="8" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false"/> 6     <style name="Sans_Large" isDefault="false" style="Sans_Normal" fontSize="10"/> 7     <style name="Sans_Bold" isDefault="false" fontName="DejaVu Sans" fontSize="8" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false"/> 8     <parameter name="reportTitle" class="java.lang.String"/> 9     <subDataset name="Country_Orders">10         <parameter name="Country" class="java.lang.String"/>11         <queryString>12             <![CDATA[SELECT ShippedDate, ShipRegion, ShipCity, Freight13             FROM Orders14             WHERE15                 ShipCountry = $P{Country} AND16                 ShippedDate IS NOT NULL]]>17         </queryString>18         <field name="ShippedDate" class="java.sql.Timestamp"/>19         <field name="ShipRegion" class="java.lang.String"/>20         <field name="ShipCity" class="java.lang.String"/>21         <field name="Freight" class="java.lang.Float"/>22     </subDataset>23     <queryString>24         <![CDATA[SELECT DISTINCT ShipCountry FROM Orders]]>25     </queryString>26     <field name="ShipCountry" class="java.lang.String"/>27     ... <!-- 省略不包含 <parameter> 和 <field> 元素的部分 -->28 </jasperReport>
复制代码

进行解析的主要 Java 代码如下:

复制代码
 1 SAXReader reader = new SAXReader(); 2 try { 3     Document doc = reader.read(new File(uploadedTemplateFilePath)); 4     // 先选取所有的parameter元素 5     List<?> parameterList = doc.selectNodes("//*[name()='parameter']"); 6     Iterator<?> parameterItr = parameterList.iterator(); 7     while(parameterItr.hasNext()) { 8         Element parameterElement = (Element)parameterItr.next(); 9         System.out.println("Current parameterElement XPath: " + parameterElement.getPath());10         String parameterName  = parameterElement.attributeValue("name");       // parameter name11         String parameterClass = parameterElement.attributeValue("class");      // parameter class12         System.out.println("parameterName = " + parameterName + "; parameterClass = " + parameterClass);13         System.out.println("--------------------------------------------------------------------------");14     }15     // 再选取所有的field元素16     List<?> fieldList = doc.selectNodes("//*[name()='field']");17     Iterator<?> fieldItr = fieldList.iterator();18     while(fieldItr.hasNext()) {19         Element fieldElement = (Element)fieldItr.next();20         System.out.println("Current fieldElement XPath: " + fieldElement.getPath());21         String fieldName  = fieldElement.attributeValue("name");       // field name22         String fieldClass = fieldElement.attributeValue("class");      // field class23         System.out.println("--------------------------------------------------------------------------");24     }25 } catch (DocumentException e) {26     e.printStackTrace();27 }
复制代码

执行以上程序解析该 jrxml 文件,在控制台输出以下结果:

结果中解析出的两个 <parameter> 和五个 <field> 都是与 jrxml 模板中对应的。

3 总结

有时候解决问题需要回到问题本身,这是我经常使用的办法。比如说这次:既然网上大家都遇到过类似问题,又没能很好解决,这时回到 Dom4j 本身,从它自身的 API 入手,果然峰回路转,解决了问题,同时也对自己知识的死角进行了修补。

希望我解决问题的思路能给大家以启发,同时也希望这篇文章能解答遇到类似问题的同学们的疑惑。

最后再啰嗦一下:第一次写技术类文章,肯定有很多不足,希望大家见谅。有意见请留言指出,我会积极改正的,谢谢 O(∩_∩)O!


附上原文地址:http://www.cnblogs.com/Payne-Wang/archive/2012/12/27/Dom4j-and-XPath.html

 

0 0
原创粉丝点击