By default, JAXB will use the Java field name as the XML element name:
Example 8-24 Sample Java Code and XML Schema
public class Customer { @XmlElement private String firstName; @XmlElement private String lastName; } <?xml version="1.0" encoding="UTF-8"?> <customer> <firstName>Bob</firstName> <lastName>Roberts</lastName> </customer>
Or, the XML name can be customized using the name attribute of the @XmlElement
annotation:
Example 8-25 Sample Java Code and XML Schema
public class Customer { @XmlElement(name="f-name") private String firstName; @XmlElement(name="l-name") private String lastName; } <?xml version="1.0" encoding="UTF-8"?> <customer> <f-name>Bob</f-name> <l-name>Roberts</l-name> </customer>
However, sometimes elements need to be mapped based on their position in the document, or based on an attribute value of an element:
<?xml version="1.0" encoding="UTF-8"?> <node> <name>Jane</name> <name>Doe</name> <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>
For cases like this, EclipseLink MOXy allows you to use XPath predicates to define an expression that will specify the XML element's name.
An XPath predicate represents an expression that will be evaluated against the element specified. For example, the XPath statement:
node[2]
Would match the second occurrence of the node element ("DEF"):
<?xml version="1.0" encoding="UTF-8"?> <data> <node>ABC</node> <node>DEF</node> </data>
Predicates can also match based on an attribute value:
node[@name='foo']
Would match the node element with the attribute name="foo" (that is, ABC). It would not match the node that contains DEF.
<?xml version="1.0" encoding="UTF-8"?> <data> <node name="foo">ABC</node> <node name="bar">DEF</node> </data>
Note: For more information on XPath Predicates, see "2.4 Predicates" of the XML Path Language (XPath) specification ( |
In the following example, our XML contains two name elements; the first occurrence of name should represent the Customer's first name, and the second name will be their last name. To map this, we will specify XPath expressions for each property that will match the appropriate XML element. Note that we also use @XmlType(propOrder)
to ensure that our elements will always be in the proper positions.
Example 8-26 Using the @XmlType(propOrder) Annotation
package example; import javax.xml.bind.annotation.*; import org.eclipse.persistence.oxm.annotations.XmlPath; @XmlRootElement @XmlType(propOrder={"firstName", "lastName"}) @XmlAccessorType(XmlAccessType.FIELD) public class Customer { @XmlPath("name[1]/text()") private String firstName; @XmlPath("name[2]/text()") private String lastName; ... }
This same configuration can be expressed in an EclipseLink XML Bindings document as follows:
Example 8-27 Using the prop-order Attribute
... <java-type name="Customer"> <xml-root-element/> <xml-type prop-order="firstName lastName"/> <java-attributes> <xml-element java-attribute="firstName" xml-path="name[1]/text()"/> <xml-element java-attribute="lastName" xml-path="name[2]/text()"/> </java-attributes> </java-type> ...
This will give us the desired XML representation:
Since EclipseLink MOXy 2.3, you can also map to an XML element based on an Attribute value. In this example, all of our XML elements are named node, differentiated by the value of their name attribute:
Example 8-29 Sample XML Schema
<?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>
We can use an XPath in the form of element-name[@attribute-name='value']
to map each Java field:
Example 8-30 Sample Mappings
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>(); ... } 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; ... } package example; import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) public class PhoneNumber { @XmlAttribute private String type; @XmlValue private String number; ... }
EclipseLink allows you to configure your one-to-one mappings so the data from the target object will appear inside the source object's XML element. Expanding on the previous example, we could map the Address information so that it would appear directly under the customer element, and not wrapped in its own element. This is referred to as a "self" mapping, and is achieved by setting the target object's XPath to . (dot).
Example 8-31 demonstrates a self mapping declared in annotations.
Example 8-31 Self Mapping Example
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(".") private Address address; @XmlPath("node[@name='phone-number']") private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); ... }
Using a self mapping, EclipseLink produces the desired XML. The street data is stored in the root node.
<?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>