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
.