最近學習了下Apache Commons項目下的一個子項目JXpath的使用,結合官方文檔與網絡上的一些入門教程小試用了一把。下面把學習內容整理了下,以便日後查看。
1 簡介
JXPath定義了一個簡單的XPath解釋器,可用於定位各種對象——JavaBeans, Maps, Servlet contexts, DOM——以及它們的混合。
JXPath的核心類是JXPathContext,所以應用幾乎總是會用到該類。
2 應用及示例
下面我們用一個示例來說明其應用,示例使用 Company --> Department --> Employee的結構來展示JXPath的應用。
Company |
Department |
Employee |
String name; List<Department> departments; |
String name; Set<Employee> employees; |
String name; int age; Map<String, String> contacts |
Company[“MS”]: Department[“Development”] Department[“Administrative”] |
Department[Development]: Employee[“Eric”] Employee[“Andy”] Department[Administrative]: Employee[“Lily”] Employee[“Lucy”] |
Employee[“Eric”]: age: 20 contacts: <home, “1234567”> <office, “7654321”> Employee[“Andy”]: age: 28 Employee[“Lily”] age: 22 Employee[“Lucy”] age: 25 |
我們共用一個JXPathContext,如下:
JXPathContext context = JXPathContext.newContext( company ); |
2.1 選取當前節點
Company c = (Company) context.getValue( "." ); |
由於我們使用的company創建的JXPathContext,所以,company是根節點,也是當前節點。使用“.”選擇器就可以選取當前節點,即company。表達式 c == company 也會返回true。
2.2 訪問屬性
String companyName = (String) context.getValue( "name" ); |
當前節點company有name屬性,所以可得company name—— MS。
2.3 謂語
Employee lily = (Employee) context.getValue( "/departments/employees[name='Lily']" ); |
謂語類似於SQL中的WHERE語句,有篩選作用,謂語被嵌在方括號中。以上代碼可以選出所有部門所有員工中叫“Lily”的員工。
謂語中可以使用運算符。
context.getValue( "/departments/employees[name='Lily1' or age=22]" ); context.getValue( "/departments/employees[name='Lily' and age=22]" );
|
2.4 變量
context.getVariables().declareVariable( "name", "Eric" ); Employee eric = (Employee) context.getValue( "/departments/employees[name=$name]" ); |
查詢中是可以使用變量的,只需要取得context關聯的變量池,聲明變量即可。
引用變量時,只需要在變量名前加前導符“$”即可。
聲明的變量不必是字符串,可以是任意對象。
2.5 寬鬆模式
context.setLenient( true ); Employee clark = (Employee) context.getValue( "/departments/employees[name='clark']" ); |
如果指定的xpath不能映射到任何已存在的節點,那麼將拋出異常。但是,這個限制在調用context.setLenient( true );後將被放寬,僅返回null,而不拋出異常。
2.6 訪問Map
context.getValue( "/departments/employees[name='Eric']/contacts/home" ); context.getValue( "/departments/employees[name='Eric']/contacts[@name='office']" ); |
我們知道Employee類中有contacts屬性,爲Map類型。名爲Eric的員工存儲了兩種聯繫方式:home和office。上面的代碼分別用兩種語法選取了兩種聯繫方式。
JXPath只支持key爲字符串的Map。
2.7 訪問集合
2.7.1 迭代單個集合
for ( Iterator<?> iter = context.iterate( "/departments" ); iter.hasNext(); ) { Department dept = (Department) iter.next(); System.out.println(dept.getName()); } |
上面的代碼會得到company下的departments的迭代器,即可遍歷所有部門。
2.7.2 迭代多個集合
for ( Iterator<?> iter = context.iterate( "/departments/employees" ); iter.hasNext(); ) { Employee emp = (Employee) iter.next(); System.out.println(emp.getName()); } |
與迭代單個集合不同,上面的代碼選取的節點不只是一個集合,它選取了所有部門下的所有員工集合。但是返回的迭代器可以一次性迭代遍歷所有員工,儘管他們在不同部門的員工集合中。
2.7.3 下標
Employee emp2 = (Employee) context.getValue( "/departments/employees[2]" ); |
對於數組或集合形式的節點,我們可以使用下標的方式進行訪問。
對Set這種無序的集合而言,它的元素順序是其迭代順序,由於迭代順序是不可預測的,所以通常不會使用下標訪問Set。
注:下標是從1開始,而不是0。
下標訪問時,根據迭代多個集合的思維來預測結果會有一些偏差。
對上面代碼而言,並沒有把所有部門的員工合併,然後選取第2個員工。相反,程序將進行路徑嘗試。首先,嘗試 /departments[1]/employees[2]能否選取節點,如果可以返回該節點;如果不能則嘗試 /departments[2]/employees[2]能否選取節點……以此類推。所以,上面代碼返回的結果應是查找到的第1個至少有2個員工的部門的第2個員工。
以上說明的是getValue()的行爲,但是對於iterate()又有點不同。
for ( Iterator<?> iter = context.iterate( "/departments/employees[2]" ); iter.hasNext(); ) { Employee emp = (Employee) iter.next(); System.out.println(emp.getName()); } |
相同的是,iterate()也會進行路徑嘗試,但不同在於它會返回所有嘗試通過的節點的集合的迭代器。對上面的代碼而言,返回的迭代器對應的集合應該是所有至少擁有2個員工的部門下的第2個員工的集合。本例中,Development部門有Andy、Eric;Administrative部門有Lucy、Lily——員工按順序列出的——那麼迭代出的員工應是Eric和Lily。
【參考】
http://www.javaworld.com/article/2077700/data-storage/java-object-queries-using-jxpath.html