EclipseLink Solutions Guide for EclipseLink
Release 2.7
  Go To Table Of Contents
 Search
 PDF

Using XPath Predicates for Mapping

This section demonstrates how the EclipseLink MOXy API uses XPath predicates to define an expression that specifiers the XML element's name. An XPath predicate is an expression that defines a specific object-to-XML mapping. As shown in previous examples, by default, JAXB will use the Java field name as the XML element name.

This section contains the following subsections:

Understanding XPath Predicates

As described above, an XPath predicate is an expression that defines a specific object-to-XML mapping when standard annotations

re not sufficient. For example, the following snippet of XML shows a <data> element with two <node> sub-elements. If you wanted to create this mapping in a Java object, you would need to specify an XPath predicate for each <node> sub-element; for example, Node[2] in the following Java:

   <java-attributes>
      <xml-element java-attribute="node" xml-path="node[1]/ABC"/>
      <xml-element java-attribute="node" xml-path="node[2]/DEF"/>
   </java-attributes>

would match the second occurrence of the node element ("DEF") in the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<data>
   <node>ABC</node>
   <node>DEF</node>
</data>

Thus, by using the XPath predicate, you can use the same attribute name for a different attribute value.

Mapping Based on Position

This mapping technique is described in Mapping Values to a Text Node by Position.

Mapping Based on an Attribute Value

Beginning with EclipseLink MOXy 2.3, you can also map to an XML element based on an Attribute value. In this exercise, you will annotate the JPA entity to render the XML document shown in Example 15-21. Note that all of the XML elements are named node but are differentiated by the value of their name attribute.

Example 15-21

<?xml version="1.0" encoding="UTF-8"?>
<node>
   <node name="first-name">Bob</node>
   <node name="last-name">Smith</node>
   <node name="address">
      <node name="street">123 A Street</node>
   </node>
   <node name="phone-number" type="work">555-1111</node>
   <node name="phone-number" type="cell">555-2222</node>
</node>

To attain this mapping, you need to declare three classes, Name, Address, and PhoneNumber and then use an XPath in the form of element-name[@attribute-name='value'] to map each Java field.

Task 1: Create the Customer Entity

To create the Customer class entity:

  1. Import the necessary JPA packages by adding the following code:

    import javax.xml.bind.annotation.*;
     
    import org.eclipse.persistence.oxm.annotations.XmlPath;
     
    
  2. Declare the Customer class and use the @XmlRootElement annotation to make it the root element. Set the XML accessor type to FIELD:

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Customer {
    
  3. Declare local to the Customer class these properties:

    • firstName (String type)

    • lastName (String)

    • Address (Address)

    For each property, set the Xpath predicate by preceding the property declaration with the annotation @XmlPath(element-name[@attribute-name='value']); for example, for firstName, you would set the XPath predicate with this statement:

    @XmlPath("node[@name='first-name']/text()")
    
  4. Also local to the Customer class, declare the phoneNumber property as a List<PhoneNumber> type and assign it the value new ArrayList<PhoneNumber>().

The Customer class should look like the snippet in Example 15-22.

Example 15-22 Customer Object Mapping to an Attribute Value

package example;
 
import javax.xml.bind.annotation.*;
 
import org.eclipse.persistence.oxm.annotations.XmlPath;
 
@XmlRootElement(name="node")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
 
    @XmlPath("node[@name='first-name']/text()")
    private String firstName;
 
    @XmlPath("node[@name='last-name']/text()")
    private String lastName;
 
    @XmlPath("node[@name='address']")
    private Address address;
 
    @XmlPath("node[@name='phone-number']")
    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
 
    ...
}

Task 2: Create the Address Entity

To create the Address class, do the following:

  1. Import the necessary JPA packages by adding the following code:

    import javax.xml.bind.annotation.*;
     
    import org.eclipse.persistence.oxm.annotations.XmlPath;
     
    
  2. Declare the Address class and set the XML accessor type to FIELD:

    @XmlAccessorType(XmlAccessType.FIELD)
    public class Address {
    

    This instance does not require the @XmlRootElement annotation as in the previous Tasks because the Address class is root not a root element in the XML document.

  3. Declare local to the Address class the String property street. Set the XPath predicate by preceding the property declaration with the annotation @XmlPath("node[@name='street']/text()").

The Address class should look like Example 15-23.

Example 15-23 Address Object Mapping to an Attribute Value

package example;
 
import javax.xml.bind.annotation.*;
 
import org.eclipse.persistence.oxm.annotations.XmlPath;
 
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
 
    @XmlPath("node[@name='street']/text()")
    private String street;
 
    ...
}

Task 3: Create the PhoneNumber Entity

To create the PhoneNumber entity:

  1. Import the necessary JPA packages by adding the following code:

    import javax.xml.bind.annotation.*;
     
    import org.eclipse.persistence.oxm.annotations.XmlPath;
     
    
  2. Declare the PhoneNumber class and use the @XmlRootElement annotation to make it the root element. Set the XML accessor type to FIELD:

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Customer {
    
  3. Create the type and string properties and define their mapping as attributes under the PhoneNumber root element by using the @XmlAttribute. annotation.

        @XmlAttribute
        private String type;
     
        @XmlValue
        private String number;
    

The PhoneNumber object should look like Example 15-24.

Example 15-24 PhoneNumber Object Mapping to an Attribute Value

package example;
 
import javax.xml.bind.annotation.*;
 
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
 
    @XmlAttribute
    private String type;
 
    @XmlValue
    private String number;
 
    ...
}

"Self" Mappings

A "self" mapping occurs on one-to-one mappings when you set the target object's XPath to "." (dot) so the data from the target object appears inside the source object's XML element. This exercise uses the example in Mapping Based on an Attribute Value to map the Address information to appear directly under the customer element and not wrapped in its own element.

To create the self mapping:

  1. Repeat Tasks 1 and 2 in Task 1: Create the Customer Entity.

  2. Declare local to the Customer class these properties:

    • firstName (String type)

    • lastName (String)

    • Address (Address)

  3. For the firstName and lastName properties, set the XmlPath annotation by preceding the property declaration with the annotation @XmlPath(element-name[@attribute-name='value']); for example, for firstName, you would set the XPath predicate with this statement:

    @XmlPath("node[@name='first-name']/text()")
    
  4. For the address property, set @XmlPath to "." (dot):

        @XmlPath(".")
        private Address address;
    
  5. Also local to the Customer class, declare the phoneNumber property as a List<PhoneNumber> type and assign it the value new ArrayList<PhoneNumber>().

The rendered XML for the Customer entity would look like Example 15-25.

Example 15-25 XML Node with Self-Mapped Address Element

<?xml version="1.0" encoding="UTF-8"?>
<node>
   <node name="first-name">Bob</node>
   <node name="last-name">Smith</node>
   <node name="street">123 A Street</node>
   <node name="phone-number" type="work">555-1111</node>
   <node name="phone-number" type="cell">555-2222</node>
</node>