[工作筆記] Dom4j 和 XPath 結合 選取 xml 中任意深度的(即所有的)特定元素


第一次寫技術相關的文章,技術含量不高,而且肯定會有諸多不足,歡迎大家提出意見,批評指正。

本文主要包含三部分:

  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, Freight
13             FROM Orders
14             WHERE
15                 ShipCountry = $P{Country} AND
16                 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 name
11         String parameterClass = parameterElement.attributeValue("class");      // parameter class
12         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 name
22         String fieldClass = fieldElement.attributeValue("class");      // field class
23         System.out.println("--------------------------------------------------------------------------");
24     }
25 } catch (DocumentException e) {
26     e.printStackTrace();
27 }

執行以上程序解析該 jrxml 文件,在控制台輸出以下結果:

結果中解析出的兩個 <parameter> 和五個 <field> 都是與 jrxml 模板中對應的。

3 總結

有時候解決問題需要回到問題本身,這是我經常使用的辦法。比如說這次:既然網上大家都遇到過類似問題,又沒能很好解決,這時回到 Dom4j 本身,從它自身的 API 入手,果然峰回路轉,解決了問題,同時也對自己知識的死角進行了修補。

希望我解決問題的思路能給大家以啟發,同時也希望這篇文章能解答遇到類似問題的同學們的疑惑。

最后再啰嗦一下:第一次寫技術類文章,肯定有很多不足,希望大家見諒。有意見請留言指出,我會積極改正的,謝謝 O(∩_∩)O!

 

                                                                                                                                                                           Payne Pandaroid Wang

                                                                                                                                                                           2012年12月27日 14:48


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM