You need to retrieve nested bean properties using an XPath expression. You need to perform an XPath query on an object graph.
Use Commons JXPath to evaluate an XPath expression against an object
graph. JXPath treats nested bean properties as if they were nested
elements in an XML document; using JXPath, the expression
a/b/c
is the equivalent of
getA( ).getB( ).getC( )
. Create a
JXPathContext
by passing an object to
JXPathContext.newContext( )
, and retrieve the
value of a nested property by passing an XPath expression to the
getValue( )
method on
JXPathContext
. The following example creates an
object graph rooted on a League
object and
retrieves nested properties using two XPath expressions:
import org.apache.commons.jxpath.JXPathContext; // Create an Object graph League league = new League( ); Team team = new Team( ); league.getTeams( ).add( team ); team.setCoach( new Person( "Coach Bob" ) ); team.getPlayers( ).add( new Person( "Player Charlie" ); team.getPlayers( ).add( new Person( "Player Ted" ); team.getPlayers( ).add( new Person( "Player Bart" ); Team team2 = new Team( ); league.getTeams( ).add( team2 ); team2.setCoach( new Person( "Coach Susan" ); team2.getPlayers( ).add( new Person( "Player Jim" ); // Query for the coach of a specific player. JXPathContext context = JXPathContext.newContext( league ); System.out.println( "** Retrieve the first name of Ted's coach");String xpath = "teams/players[firstName = 'Player Ted']/../coach/firstName";
Object value = context.getValue( xpath ); System.out.println( value ); // Query for the players of a specific coach context = JXPathContext.newContext( league ); System.out.println( "** Retrieve the players on Coach Susan's team");value = context.getValue( "teams/coach[firstName = 'Coach Susan']/../players" );
System.out.println( value );
This example creates a League
with two
Team
objects stored in a List
.
Each Team
object has a coach
property of type Person
, and a
players
property, which is a
List
of Person
objects. A
JXPathContext
is created by passing
league
to JXPathContext.newContext( )
, and two XPath queries are executed by passing query
strings to getValue( )
. The first XPath query
returns the firstName
of Ted’s
coach, and the second XPath query returns the
players
List
of the team Susan
coaches. This example produces the following output:
Retrieve the first name of Ted's coach Coach Bob Retrieve the players on Coach Susan's team [com.discursive.jccook.xml.jxpath.Person@173831b]
XPath is generally used by select nodes in an XML document, and you
may have used it to transform XML with Extensible Stylesheet Language
Transformations (XSLT). In this example, XPath is used in a somewhat
unconventional manner as a query to filter and select objects based
on the values of deeply nested properties. The first
query—teams/players[firstname =
`Player
Ted']/../coach/firstName
—
is
evaluated using the League
object as the current
node, and if one were to evaluate the XPath expressions
self( ) or ., you would
retrieve the League
object passed to
newContext( )
. When the previous example retrieved
the first name of Ted’s coach, JXPath iterated
through the team
List
, and
located the matching Player
and
Coach
object. The execution of the first XPath
expression in the previous example is equivalent to the following
code, which iterates through the Team
and
Player
lists:
String firstName = null; Iterator teamIterator = league.getTeams( ).iterator( ); while( teamIterator.hasNext( ) ) { Team team = (Team) teamIterator.next( ); Iterator playerIterator = team.getPlayers( ).iterator( ); while( playerIterator.hasNext( ) ) { Player player = (Player) playerIterator.next( ); if( player.getFirstName( ).equals( "Player Ted" ) ) { firstName = team.getCoach( ).getFirstName( ); } } }
The ability to filter a complex object graph with a simple expression can help you avoid writing tedious code to iterate through doubly nested collections to compare property values.
Commons JXPath can also be used to reference objects at a specific
index in a List
or an array, or objects by a known
key in a Map
. Recipe 12.4
demonstrates how to use JXPath to reference items in a
Map
, and Recipe 12.3
demonstrates
the use of JXPath to reference an
item at a specific index in a List
.
3.142.212.160