12.2. Querying an Object Graph with XPath

Problem

You need to retrieve nested bean properties using an XPath expression. You need to perform an XPath query on an object graph.

Solution

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]

Discussion

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.

See Also

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.212.160