In a recent project, I needed an easy way to traverse Java object trees and extract values from the objects. Instead of continually going through huge iterator-if-else setups, I wanted a tool that would allow me to simply say, "I want the object with id=X, and from that object, I need the value of property A." In essence, I needed an object-querying tool.
[@more@]JXPath is such an object-query tool. It is an Apache Commons component that enables you to query complex object trees using the well-known XPath expression language. I employed JXPath widely in my project, and it sped up things considerably, making value-extraction algorithms a breeze.
However, JXPath is not widely documented. Because I was exploring the component in-depth anyway, I decided to write down my findings in an extensive JXPath tutorial, which you can find on my Web site. This article is a shorthand version of that tutorial to get you started with JXPath quickly.
Note: You can download the accompanying sample code from Resources.
Example model
For illustration purposes, we will use a simple model: a company with various departments, each with various employees. Here's the class model:
Company class diagram. Click on thumbnail to view full-sized image.
Naturally, we need some sample data for the model:
Company | Department | Employee (name, job title, age) |
Acme Inc. | Sales | Johnny, Sales rep, 45 Sarah, Sales rep, 33 Magda, Office assistant, 27 |
Accounting | Steve, Head controller, 51 Peter, Assistant controller, 31 Susan, Office assistant, 27 |
With that in place, let's start using JXPath!
Executing simple JXPath queries
The simplest query possible extracts a single object from the object tree. For example, to retrieve Company
, use the following code:
JXPathContext context = JXPathContext.newContext(company);
Company c = (Company)context.getValue(".");
The first line shows the creation of a context
, the starting point for all JXPath's XPath expressions in the object tree (comparable to the rootnode
element in an XML document). The second line of code executes the actual query. Since our context
starts at the company level, to retrieve the Company
object, we simply use the current-element selector '.'
.
Using predicates and variables
An Employee
is a child object of a Department
. To retrieve the Employee
named "Johnny" use the following code (Company
is still context
's starting point):
Employee emp = (Employee)context.getValue("/departmentList/employees[name='Johnny']");
Basically, the code reads: "Search all Department
s from the beginning for the Employee
object of which the name
attribute has the value 'Johnny'
."
The above code snippet illustrates how to use a predicate to search objects by using particular values. Using predicates is comparable to using the WHERE clause in SQL. We can even combine multiple predicates in one query:
Employee emp =
(Employee)context.getValue("/departmentList/employees[name='Susan' and age=27]");
Unless you're using an ad-hoc, one-time-only query, implementing hard-coded queries usually isn't feasible. It is better to define a reusable query that you can then execute with different parameters. To accommodate parameterized querying, JXPath supports variables in queries. Using variables, the code above now looks like this:
context.getVariables().declareVariable("name", "Susan");
context.getVariables().declareVariable("age", new Integer(27));
Employee emp =
(Employee)context.getValue("/departmentList/employees[name=$name and age=$age]");
Iterating over collections
JXPath can provide an iterator over all objects retrieved by a query, much like iterating a result-set. The following snippet shows how you can iterate over all Department
s:
for(Iterator iter = context.iterate("/departmentList"); iter.hasNext();){
Department d = (Department)iter.next();
//...
}
To retrieve all Employee
s from all Department
s and iterate over them:
for(Iterator iter = context.iterate("/departmentList/employees"); iter.hasNext();){
Employee emp = (Employee)iter.next();
//...
}
To retrieve all Employee
s older than 30 years from the sales department:
for(Iterator iter = context.iterate
("/departmentList[name='Sales']/employees[age>30]"); iter.hasNext();){
Employee emp = (Employee)iter.next();
//...
}
And the above example with variables:
context.getVariables().declareVariable("deptName", "Sales");
context.getVariables().declareVariable("minAge", new Integer(30));
for(Iterator iter = context.iterate("/departmentList
[name=$deptName]/employees[age>$minAge]"); iter.hasNext();){
Employee emp = (Employee)iter.next();
//...
}
Those two last code snippets also demonstrate the use of several predicates within one XPath query.
Pointers
A Pointer
is a JXPath utility object that represents a reference to the location of an object in the object tree. For example, a Pointer
might refer to "the first employee of the second department." Compared to objects retrieved directly from the tree, Pointer
s offer additional functions like the execution of relative queries through relative contexts (more on this later).
Using Pointers
Having a Pointer
refer to an object in the object tree is almost identical to directly retrieving objects:
JXPathContext context = JXPathContext.newContext(company);
Pointer empPtr = context.getPointer("/departmentList[name='Sales']/employees[age>40]");
System.out.println(empPtr);
//output: /departmentList[1]/employees[1]
System.out.println(((Employee)empPtr.getValue()).getName());
//output: Johnny
Note that the Pointer
's output demonstrates that a Pointer
describes an object's location, rather than the object itself. Also note that the actual object the Pointer
refers to can be retrieved through the Pointer
's getValue()
method.
Pointers can also be iterated over, as the following snippet demonstrates:
for(Iterator iter = context.iteratePointers("/departmentList[name='Sales']
/employees[age>30]"); iter.hasNext();){
Pointer empPtr = (Pointer)iter.next();
//...
}
Relative context and relative queries
Since a Pointer
describes a location, it can be used as a reference point for navigating through the entire object tree. To do that, use the Pointer
as the root object (Remember using the Company
object for that earlier?) in a so called relative context. From this relative context, you can query the entire object tree by executing relative queries. This advanced use of Pointer
s offers great flexibility as the examples below illustrate.
To begin, here's how you create a relative context:
for(Iterator iter = context.iteratePointers("/departmentList[name='Sales']
/employees[age>30]"); iter.hasNext();){
Pointer empPtr = (Pointer)iter.next();
JXPathContext relativeContext = context.getRelativeContext(empPtr);
}
In this code snippet, a new relative context is created for consecutive employee
pointers.
Using the relative context, XPath queries can be executed on the entire object tree of siblings, children, and parent/grandparent objects, as the following snippet demonstrates:
//Current employee
Employee emp = (Employee)relativeContext.getValue(".");
//Employee name
String name = (String)relativeContext.getValue("./name");
//Name of the Department this Employee belongs to (a parent object)
String deptName = (String)relativeContext.getValue("../name");
//Name of the Company this Employee belongs to (a 'grandparent' object)
String compName = (String)relativeContext.getValue("../../name");
//All coworkers of this Employee (sibling objects)
for(Iterator empIter = relativeContext.iterate("../employees"); empIter.hasNext();){
Employee colleague = (Employee)empIter.next();
//...
}
Summary
JXPath is an extremely useful tool for traversing, navigating, and querying complex object trees. Because it uses the XPath expression language for its queries, a large body of reference material is available to help you build efficient yet complex object-retrieval queries. Even more flexibility is added by using Pointer
s and relative contexts.
This brief article only scratches the surface of JXPath's possibilities, for a more in depth discussion with more advanced usage examples, read my full tutorial.
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/71047/viewspace-996766/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/71047/viewspace-996766/