使用JXPath访问java对象、集合和XML文件

一、JXPath简介

JXPath是apache公司提供的XPath的java实现,属于jakarta的一部分,最新的版本是1.1,JXPath的主要功能在于一组java类库来使用XPath的方式访问符合JavaBeans规范的java类、java集合(Collections)、其他具有动态属性的对象(如Map、ServletContext等),同时提供了一套扩展机制使我们可以增加对这些对象之外的其他对象模型的支持。

[注]

1、关于XPath的更多内容请大家访问XPath的主页

asp">http://www.w3schools.com/xpath/default.asp

2、关于JXPath的更多内容请大家访问JXPath的主页

http://jakarta.apache.org/commons/jxpath/index.html

二、环境准备

1、下载JXPath

JXPath最新版本的二进制代码下载地址为

http://apache.linuxforum.net/dist/jakarta/commons/jxpath/binaries/commons-jxpath-1.1.zip

2、下载eclipse

作者使用了eclipse作为开发工具,请大家到http://www.eclipse.org/下载eclipse的最新版本。

3、下载Tomcat

作者使用了Tomcat作为Web容器来演示如何使用JXPath访问ServletContext中的对象,请大家到jakarta.apache.org下载Tomcat的最新版本。

下面的几个章节将详细的演示如何使用JXPath来访问各种各样的对象,同时将演示如何通过JXPath来创建对象、修改对象的属性等功能。

三、使用JXPath访问对象内容3.1 访问JavaBean的属性

1、准备一个符合要求的Java类

作者制作了一个Company类,它包括3个属性:ID、Name和Address,代码如下:package org.vivianj.jxpath.examples.pub;
import java.util.Comparator;
import org.apache.log4j.Logger;
public class Company implements Comparator{
public static Logger logger = Logger.getLogger(Company.class);
private String name = "";
private int id = 0;
private String address = "";
public void setName(String p_name){
this.name = p_name;
}
public void setId(int p_id){
this.id = p_id;
}
public void setAddress(String p_address){
this.address = p_address;
}
public String getName(){
return this.name;
}
public int getId(){
return this.id;
}
public String getAddress(){
return this.address;
}
public int compare(Object o1, Object o2){
return 0;
}
public boolean equals(Object obj) {
boolean result = false;
if (obj instanceof Company){
Company company = (Company) obj;
if (company.getId()==this.id
&& company.getName().equals(this.getName())
&& company.getAddress().equals(this.getAddress()))
result = true;
}
return result;
}
}

2、使用JXPath来访问该java类的属性

现在我们使用JXPath来访问这个类的属性,测试代码如下://实例化一个Company对象
Company company = new Company();
//设置该对象的各个属性
company.setId(1);
company.setName("vivianj组织");
company.setAddress("www.vivianj.org");
//初始化JXPath的上下文环境
JXPathContext context = JXPathContext.newContext(company);
//使用XPath语法来访问该对象的属性
//getValue方法的参数"name"、"id"、"address"使用了XPath的语法,
//他们分别代表要访问company对象的属性name、id、address
String name = (String)context.getValue("name");
Integer id = (Integer) context.getValue("id");
String address = (String)context.getValue("address");

3.1.1 Lenient 访问模式

在上面的访问方式中有可能会出现这样的情况:如果你要访问的属性不是这个Java类的属性,那么执行过程中系统会报出一个违例-- org.apache.commons.jxpath.JXPathException: No value for xpath: xxx(xxx是你要访问的属性的名称)。

这种情况对于程序的稳定性、健壮性是有害的,这时候我们应该使用JXPath提供的Lenient 访问模式来避免出现这样的情况,在Lenient 访问模式下,如果你访问了不存在的属性,系统会返回一个null,而不是抛出一个违例。

要使用Lenient 访问模式非常简单,只需要在代码中增加context.setLenient(true)调用就可以了,具体操作如下:

//实例化一个Company对象
Company company = new Company();
//设置该对象的各个属性
company.setId(1);
company.setName("vivianj组织");
company.setAddress("www.vivianj.org");
//初始化JXPath的上下文环境
JXPathContext context = JXPathContext.newContext(company);
//通知系统使用Lenient 访问模式
context.setLenient(true)
//使用XPath语法来访问该对象的属性
String name = (String)context.getValue("name1");

[注] name1 不是Company类的属性,但是由于使用了Lenient 访问模式,所以系统返回null。

3.2 访问嵌套属性

3.1中的例子演示了如何访问类的简单类型属性,如果类的属性本身就是一个类类型,情况会怎么样呢,下面的例子将演示这种访问方式:

1、准备Association类

Association类有一个属性company,他本身是Company类类型package org.vivianj.jxpath.examples.pub;
import java.util.ArrayList;
import java.util.Collection;
public class Association {
private Company company;
public Company getCompany(){
return this.company;
}
public void setCompany(Company p_company){
this.company = p_company;
}
}

2、用JXPath访问嵌套属性//实例化Association类
Association association = new Association();
//实例化Company类
Company company = new Company();
company.setId(1);
company.setName("vivianj组织");
company.setAddress("www.vivianj.org");
//设置Association对象的company属性
association.setCompany(company);
//初始化JXPath上下文
JXPathContext context = JXPathContext.newContext(association);
//使用Lenient访问模式访问嵌套属性
context.setLenient(true);
//通过JXPath方法获得指定属性的值
//其中getValue方法的参数"company/name"的
//第一部分company代表Association的属性company,
//第二部分("/"符号后面的部分)name代表是company对象的属性
String name = (String) context.getValue("company/name");

3.3 访问Java集合

JXPath可以访问Java集合的内容,这些集合包括java数组、Collection类及其子类,他们的访问方式基本类似,详细的情况请参照下面的程序代码:

1、扩展Association类,增加一个提供Company对象的数组的方法

给Association类增加一个方法getCompanysInArray方法,方法的签名和内容如下:public Company[] getCompanysInArray(){
for (int i = 0 ; i < 5 ; i++){
//实例化新的Company对象
Company comp = new Company();
comp.setId(i);
comp.setName("Name" + i );
comp.setAddress("address" + i);
//将实例化的对象赋值给到数组的对应元素
companysInArray[i] = comp;
}
return companysInArray;
}

2、扩展Association类,增加一个提供Company对象的Collection的方法

给Association类增加一个方法getCompanysInCollection方法,方法的签名和内容如下:public Collection getCompanysInCollection(){
for (int i = 0 ; i < 5 ; i++){
//实例化新的Company对象
Company comp = new Company();
comp.setId(i);
comp.setName("Name" + i );
comp.setAddress("address" + i);
//将实例化的对象增加到Collection中
companysInCollection.add(comp);
}
return companysInCollection;
}

3.3.1 访问方法

通过JXPath访问数组的详细代码如下:

//实例化Association类
Association association = new Association();
//初始化JXPath上下文
JXPathContext context = JXPathContext.newContext(association);
//使用Lenient访问模式访问嵌套属性
context.setLenient(true);
//通过JXPath语法访问数组下标为4的记录的name属性
//getValue方法的参数"companysInArray[5]/name"中的
//部分companysInArray是Association的属性,
//5代表访问数组中第5个元素,name表示第五个元素的属性名
String name = (String) context.getValue("companysInArray[5]/name");
//通过XPath语法访问集合中第4条记录的name属性
//getValue方法的参数" companysInColletion[5]/name"中的
//部分companysInColletion是Association的属性名,
//5代表访问集合中第5个元素,name表示第五个元素的属性名
String name = (String) context.getValue("companysInColletion[5]/name");

[注] XPath访问数组或者集合时,数组或者集合的下标是从1开始,这点和java语言中规定的从0开始有点不同

3.3.2 获取多条记录

既然是访问集合数据,那么经常会出现这样的需求:需要获得符合条件的多条记录。这种情况使用JXPath也非常方便,使用context对象的iterator方法加上相应的XPath信息就可以了,操作后返回的内容保存在Iterator对象中,非常方便就可以访问。具体的代码如下:

1、按记录所在的位置获取//实例化Association类
Association association = new Association();
//实例化JXPath上下文
JXPathContext context = JXPathContext.newContext(association);
//获得数组中下标大于3的所有记录
//iterator方法的参数companysInArray [position() > 3]使用了XPath的语法
//其中的companysInArray是Association对象的属性,他是一个数组
// position()是XPath中的内置函数,获得记录在数组中的下标
Itarator companysInArray =
context.iterate("companysInArray [position() > 3]");
//获得集合中所处位置大于3的所有记录
//iterator方法的参数companysInCollection [position() > 3]使用了XPath的语法
//其中的companysInCollection是Association对象的属性
//他是一个Collection类型或者是其子类型的一个实例
//position()是XPath中的内置函数,获得记录在集合中的位置
Itarator companysInCollection =
context.iterate("companysInCollection [position() > 3]");

2、按指定的规则获取//实例化Association类
Association association = new Association();
//实例化JXPath上下文
JXPathContext context = JXPathContext.newContext(association);
//获得数组中对象的name属性为'name3'的所有对象
//iterator方法的参数companysInArray [name='name3']使用了XPath的语法
//其中的companysInArray是Association对象的属性,他是一个数组
//name='name3'是条件表达式,表示返回对象的name属性值必须是name3
Itarator companysInArray =
context.iterate("companysInArray [name='name3']");
//获得集合中对象的name属性为'name2'的所有对象
//iterator方法的参数companysInCollection [name='name3']使用了XPath的语法
//其中的companysInCollection是Association对象的属性
//他是一个Collection类型或者是其子类型的一个实例
//name='name3'是条件表达式,表示返回对象的name属性值必须是name3
Itarator companysInCollection =
context.iterate("companysInCollection [name='name3']");

3.4 访问Map对象的内容

1、准备符合条件的java类package org.vivianj.jxpath.examples.pub;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.jxpath.JXPathContext;
public class MyMapSource {
private Map map = new HashMap();
public MyMapSource(){
map.put("id",new Integer(5));
map.put("name","name");
}
public Map getMapSource(){
return this.map;
}
}

2、使用JXPath访问Map的内容//实例化MyMapSource对象
MyMapSource myMapSource = new MyMapSource();
//实例化JXPath上下文
JXPathContext context = JXPathContext.newContext(myMapSource);
//通过JXPath访问Map对象的内容
// getValue方法的参数使用了XPath语法
// mapSource/id中的mapSource表示MyMapSource对象的属性,
//他是一个Map类型的对象,id表示获取该Map对象的id字段
Integer id = (Integer) context.getValue("mapSource/id");

3.5 访问XML文件

1、编写自己的XML文件


sun


18 #,WenShan Road



ibm

18 #,WenEr Road


2、编写一个类,返回符合条件的company内容package org.vivianj.jxpath.examples.pub;
import java.net.URL;
import org.apache.commons.jxpath.Container;
import org.apache.commons.jxpath.xml.DocumentContainer;
public class Companys {
private Container companys = null;
public Container getCompanys(){
if (companys == null){
//获取XML文件的内容
URL url = getClass().getResource("companys.xml");
//将XML的内容绑定到companys对象
companys = new DocumentContainer(url);
}
return companys;
}
}

3、使用JXPath访问XML文件的内容//实例化Companys对象
Companys companys = new Companys();
//初始化JXPath上下文
JXPathContext context = JXPathContext.newContext(companys);
//获得指定记录的子元素的内容
/*getValue方法的参数
"companys/companys/company[@id = '101']/address/street"
使用了XPath语法
其中的第一个companys表示访问Companys对象的companys属性
第二个companys表示访问XML数据中的companys元素
company、address、street都是xml中的元素的名字
@id = '101'是一个条件表达式,表示符合条件的company元素的id属性必须是101
*/
String street = (String)context.getValue(
"companys/companys/company[@id = '101']/address/street");
//通过JXPath获取xml元素的属性的值
logger.debug("id=" +
context.getValue("companys/companys/company[@id = '101']/@id"));
//通过JXPath获取xml元素的子元素的值
logger.debug("p_id=" +
context.getValue("companys/companys/company[name = 'sun']/name"));

[注] 通过JXPath访问xml内容时,如果访问属性,必须增加一个@符号,以示区别

四、总结

JXPath是apache组织提供的一个XPath的java实现,目前最新的版本是1.1,通过JXPath提供的丰富的类库,使用者可以很简单的使用XPath语法来访问java对象、集合、xml内容、web应用环境下的各种对象等。

本文中作者一开始简单的介绍了JXPath的相关信息,接下来的章节中,作者结合实例,详细的演示了如何通过JXPath提供的丰富的类库访问java对象、集合和XML文件的详细过程,同时给出了简单的注释,希望能够帮助大家进入JXPath的精彩世界。JXPath的强大功能远不止此,请大家关注作者的后续文章。


JXPath Interprets XPath Syntax on Java Object Graphs

JXPath uses an intuitive interpretation of the xpath syntax in the context of Java object graphs. Here are some examples:

Example 1: JavaBean Property Access

JXPath can be used to access properties of a JavaBean.
 
 
public class Employee { public String getFirstName(){ ... } } Employee emp = new Employee(); ... JXPathContext context = JXPathContext.newContext(emp); String fName = (String)context.getValue("firstName");
In this example, we are using JXPath to access a property of the  emp  bean. In this simple case the invocation of JXPath is equivalent to invocation of getFirstName() on the bean.

Example 2: Nested Bean Property Access

JXPath can traverse object graphs:
 
 
public class Employee { public Address getHomeAddress(){ ... } } public class Address { public String getStreetNumber(){ ... } } Employee emp = new Employee(); ... JXPathContext context = JXPathContext.newContext(emp); String sNumber = (String)context.getValue("homeAddress/streetNumber");
In this case XPath is used to access a property of a nested bean.

A property identified by the xpath does not have to be a "leaf" property. For instance, we can extract the whole Address object in above example:

 
 
Address addr = (Address)context.getValue("homeAddress");

Example 3: Collection Subscripts

JXPath can extract elements from arrays and collections.
 
 
public class Integers { public int[] getNumbers(){ ... } } Integers ints = new Integers(); ... JXPathContext context = JXPathContext.newContext(ints); Integer thirdInt = (Integer)context.getValue("numbers[3]");
A collection can be an arbitrary array or an instance of java.util. Collection.

Note: in XPath the first element of a collection has index 1, not 0.

Example 4: Map Element Access

JXPath supports maps. To get a value use its key.
 
 
public class Employee { public Map getAddresses(){ return addressMap; } public void addAddress(String key, Address address){ addressMap.put(key, address); } ... } Employee emp = new Employee(); emp.addAddress("home", new Address(...)); emp.addAddress("office", new Address(...)); ... JXPathContext context = JXPathContext.newContext(emp); String homeZipCode = (String)context.getValue("addresses/home/zipCode");
Often you will need to use the alternative syntax for accessing Map elements:
 
 
String homeZipCode = (String) context.getValue("addresses[@name='home']/zipCode");
In this case, the key can be an expression, e.g. a variable.
Note: At this point JXPath only supports Maps that use strings for keys.
Note: JXPath supports the extended notion of Map: any object with dynamic properties can be handled by JXPath provided that its class is registered with the  JXPathIntrospector .

Example 5: Retrieving Multiple Results

JXPath can retrieve multiple objects from a graph. Note that the method called in this case is not  getValue , but  iterate .
 
 
public class Author { public Book[] getBooks(){ ... } } Author auth = new Author(); ... JXPathContext context = JXPathContext.newContext(auth); Iterator threeBooks = context.iterate("books[position() < 4]");
This returns a list of at most three books from the array of all books written by the author.

Example 6: Setting Properties

JXPath can be used to modify property values.
 
 
public class Employee { public Address getAddress() { ... } public void setAddress(Address address) { ... } } Employee emp = new Employee(); Address addr = new Address(); ... JXPathContext context = JXPathContext.newContext(emp); context.setValue("address", addr); context.setValue("address/zipCode", "90190");

Example 7: Creating objects

JXPath can be used to create new objects. First, create a subclass of  AbstractFactory  and install it on the JXPathContext. Then call  createPathAndSetValue()  instead of "setValue". JXPathContext will invoke your AbstractFactory when it discovers that an intermediate node of the path is  null . It will not override existing nodes.
 
 
public class AddressFactory extends AbstractFactory { public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index){ if ((parent instanceof Employee) && name.equals("address"){ ((Employee)parent).setAddress(new Address()); return true; } return false; } } JXPathContext context = JXPathContext.newContext(emp); context.setFactory(new AddressFactory()); context.createPathAndSetValue("address/zipCode", "90190");

Example 8: Using Variables

JXPath supports the notion of variables. The XPath syntax for accessing variables is  "$varName" .
 
 
public class Author { public Book[] getBooks(){ ... } } Author auth = new Author(); ... JXPathContext context = JXPathContext.newContext(auth); context.getVariables().declareVariable("index", new Integer(2)); Book secondBook = (Book)context.getValue("books[$index]");
You can also set variables using JXPath:
 
 
context.setValue("$index", new Integer(3));
Note: you can only  change  the value of an existing variable this way, you cannot  define  a new variable.

When a variable contains a JavaBean or a collection, you can traverse the bean or collection as well:

 
 
... context.getVariables().declareVariable("book", myBook); String title = (String)context.getValue("$book/title); Book array[] = new Book[]{...}; context.getVariables().declareVariable("books", array); String title = (String)context.getValue("$books[2]/title);

Example 9: Using Nested Contexts

If you need to use the same set of variable while interpreting XPaths with different beans, it makes sense to put the variables in a separate context and specify that context as a parent context every time you allocate a new JXPathContext for a JavaBean.
 
 
JXPathContext varContext = JXPathContext.newContext(null); varContext.getVariables().declareVariable("title", "Java"); JXPathContext context = JXPathContext.newContext(varContext, auth); Iterator javaBooks = context.iterate("books[title = $title]");

Using Custom Variable Pools

By default, JXPathContext creates a HashMap of variables. However, you can substitute a custom implementation of the Variables interface to make JXPath work with an alternative source of variables. For example, you can define implementations of Variables that cover a servlet context, HTTP request or any similar structure.

Example 10: Using Standard Extension Functions

Using the standard extension functions, you can call methods on objects, static methods on classes and create objects using any constructor. The class names should be fully qualified.

Here's how you can create new objects:

 
 
Book book = (Book) context.getValue( "org.apache.commons.jxpath.example.Book.new ('John Updike')");
Here's how you can call static methods:
 
 
Book book = (Book) context.getValue( "org. apache.commons.jxpath.example.Book.getBestBook('John Updike')");
Here's how you can call regular methods:
 
 
String firstName = (String)context.getValue("getAuthorsFirstName($book)");
As you can see, the target of the method is specified as the first parameter of the function.

Example 11: Using Custom Extension Functions

Collections of custom extension functions can be implemented as  Functions  objects or as Java classes, whose methods become extenstion functions.

Let's say the following class implements various formatting operations:

 
 
public class Formats { public static String date(Date d, String pattern){ return new SimpleDateFormat(pattern).format(d); } ... }
We can register this class with a JXPathContext:
 
 
context.setFunctions(new ClassFunctions(Formats.class, "format")); ... context.getVariables().declareVariable("today", new Date()); String today = (String)context.getValue("format:date($today, 'MM/dd/yyyy')");
You can also register whole packages of Java classes using PackageFunctions.

Also, see FunctionLibrary, which is a class that allows you to register multiple sets of extension functions with the same JXPathContext.



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值