3 Mapping Type Levels

This chapter includes the following sections:

3.1 Defining the Default Root Element

At least one of your mapped classes must have a default root element defined. This tells EclipseLink what the top-level root of your XML document will be. Consider the Customer and Address classes shown in Figure 3-1:

Figure 3-1 Sample Mapped Classes

Sample Mapped Classes
Description of "Figure 3-1 Sample Mapped Classes"

These classes correspond to the XML schema shown in Example 3-1. The schema contains a top-level element of type customer-type, therefore our Customer class will need to have a default root element specified.

Example 3-1 Sample XML Schema

<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xsd:complexType name="address-type">
      <xsd:sequence>
         <element name="street" type="xsd:string"/>
         <element name="city" type="xsd:string"/>
      </xsd:sequence>
   </xsd:complexType>
 
   <xsd:element name="customer" type="customer-type"/>
 
   <xsd:complexType name="customer-type">
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="billing-address" type="address-type"/>
         <xsd:element name="shipping-address" type="address-type"/>
      </xsd:sequence>
   </xsd:complexType>
</xsd:schema>
 

Example 3-2 shows how to annotate your Java class to specify a default root element. All that is needed is the standard JAXB @XmlRootElement annotation.

Example 3-2 Using the @XmlRootElement Annotation

package example;
 
import javax.xml.bind.annotation.*;
 
@XmlRootElement
public class Customer {
   private String name;
 
   @XmlElement(name="billing-address")
   private Address billingAddress;
 
   @XmlElement(name="shipping-address")
   private Address shippingAddress;
 
   ...
}
 

Example 3-3 shows how specify a default root element in EclipseLink's OXM metadata format.

Example 3-3 Specifying a Default Root Element

...
<java-type name="Customer">
   <xml-root-element/>
   <java-attributes>
      <xml-element java-attribute="name"/>
      <xml-element java-attribute="billingAddress" name="billing-address"/>
      <xml-element java-attribute="shippingAddress" name="shipping-address"/>
   </java-attributes>
</java-type>
...
 

3.1.1 Customizing the Default Root Element

In Example 3-2, our class is called Customer, and our root element name in XML is customer. By default, when @XmlRootElement is specified, the name of the class has its first letter lower-cased, and this is set as the root element name. If, however, the element name in XML is different from the Java class name, a name attribute can be included in the annotation (as in Example 3-4) or OXM metadata (as in Example 3-5):

Example 3-4 Using Annotations

package example;
 
import javax.xml.bind.annotation.*;
 
@XmlRootElement(name="my-customer")
public class Customer {
   private String name;
 
   @XmlElement(name="billing-address")
   private Address billingAddress;
 
   @XmlElement(name="shipping-address")
   private Address shippingAddress;
 
   ...
}
 

Example 3-5 Using OXM Metadata

...
<java-type name="Customer">
   <xml-root-element name="my-customer"/>
   <java-attributes>
      <xml-element java-attribute="name"/>
      <xml-element java-attribute="billingAddress" name="billing-address"/>
      <xml-element java-attribute="shippingAddress" name="shipping-address"/>
   </java-attributes>
</java-type>
...
 

For more information on JAXB name-binding algorithms, see "Appendix D: Binding XML Names to Java Identifiers" of the Java Architecture for XML Binding (JAXB) Specification (http://jcp.org/en/jsr/detail?id=222).

3.1.2 Understanding How EclipseLink Uses the Default Root Element

When an instance of the Customer class is persisted to XML, the EclipseLink runtime performs the following:

  • Gets the default root element. The Customer class instance corresponds to the root of the XML document. The EclipseLink runtime uses the default root element (customer) specified in either annotations or OXM to start the XML document. EclipseLink then uses the mappings on the class to marshal the object's attributes.

    <customer>
       <name>...</name>
    </customer>
     
    
  • When the EclipseLink runtime encounters an object attribute such as billingAddress, it checks the mapping associated with it to determine with what element (billing-address) to continue.

    <customer>
       <name>...</name>
       <billing-address/>
    </customer>
     
    
  • The EclipseLink runtime checks the mapping's reference descriptor (Address) to determine what attributes to persist.

    <customer>
       <name>...</name>
       <billing-address>
          <street>...</street>
          <city>...</city>
       </billing-address>
    </customer>
    

3.2 Setting Up Namespace Information

Most XML documents are qualified with a namespace. You can namespace-qualify elements of your Java class at the following levels:

In most cases, package-level annotation is sufficient. You can use the other levels to customize your document. Use the @XmlSchema annotation to specify the namespace.

3.2.1 Qualifying at the Package Level

Use the @XmlSchema annotation on the package to set a default namespace and specify that all elements in the package are qualified with the namespace. This information is specified in a special Java source file, package-info.java.

Example 3-6 Using Annotations

@XmlSchema(
   namespace="http://www.example.org/package",
   elementFormDefault=XmlNsForm.QUALIFIED)
package example;
 
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
 

This can be defined in EclipseLink XML Bindings as follows:

Example 3-7 Using OXM Metadata

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
    <xml-schema
        element-form-default="QUALIFIED"
        namespace="http://www.example.org/package">
    </xml-schema>
 
    <java-types>
        <java-type name="Customer">
        ...
 
</xml-bindings>
 

Using a simple Customer class, Example 3-6 and Example 3-7 will produce the following XML:

<customer xmlns="http://www.example.org/package">
   <name>Jane Doe</name>
   <account>36328721</account>
</customer>
 

All elements are qualified with the http://www.example.org/package namespace.

3.2.2 Qualifying at the Type Level

Type level annotations will override the package level namespace.

Example 3-8 Using Annotations

package example;
 
@XmlRootElement
@XmlType(namespace="http://www.example.org/type")
public class Customer {
   private String name;
 
   private String account;
 
   ...
}
 

This can be defined in EclipseLink XML Bindings as follows:

Example 3-9 Using XML Bindings File

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
    <xml-schema
        element-form-default="QUALIFIED"
        namespace="http://www.example.org/package">
    </xml-schema>
 
    <java-types>
        <java-type name="Customer">
            <xml-type namespace="http://www.example.org/type" />
            <java-attributes>
                <xml-element java-attribute="name" />
                <xml-element java-attribute="account" />
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>
 

This will produce the following XML:

<custom xmlns="http://www.example.org/package" xmlns:ns0="http://www.example.org/type">
   <ns0:name>Bob</ns0:name>
   <ns0:account>1928712</ns0:account>
</custom>
 

Elements inside the Customer type are qualified with the http://www.example.org/type namespace.

3.2.3 Qualifying at the Field/Property Level

You can override the package or type namespaces at the property/field level. All attribute and element annotations accept the namespace parameter.

Example 3-10 Overriding the Namespace

package example;
 
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace="http://www.example.org/type")
public class Customer {
   private String name;
 
   @XmlElement(namespace="http://www.example.org/property")
   private String account;
 
   ...
}
 

This can be defined in EclipseLink XML Bindings as follows:

Example 3-11 Sample Bindings File

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
    <xml-schema
        element-form-default="QUALIFIED"
        namespace="http://www.example.org/package">
    </xml-schema>
 
    <java-types>
        <java-type name="Customer">
            <xml-type namespace="http://www.example.org/type" />
            <java-attributes>
                <xml-element java-attribute="name" />
                <xml-element java-attribute="account" namespace="http://www.example.org/property" />
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>
 

This will produce the following XML:

<custom xmlns="http://www.example.org/package" xmlns:ns1="http://www.example.org/property"
   xmlns:ns0="http://www.example.org/type">
   <ns0:name>Bob</ns0:name>
   <ns1:account>1928712</ns1:account>
</custom>
 

Only the account element is qualified with the http://www.example.org/property namespace.

3.3 Specifying Inheritance

EclipseLink MOXy provides several ways to represent your inheritance hierarchy in XML:

3.3.1 Using xsi:type

By default, EclipseLink will use the xsi:type attribute to represent inheritance in XML.

In this example an abstract super class (ContactInfo) contains all types of contact information. Address and PhoneNumber are the concrete implementations of ContactInfo.

Example 3-12 Sample Java Classes

public abstract class ContactInfo {
}
 
public class Address extends ContactInfo {
 
   private String street;
   ... 
 
}
 
public class PhoneNumber extends ContactInfo {
 
   private String number;
   ...
 
}
 

Because the Customer object can have different types of contact information, its property refers to the superclass.

@XmlRootElement
public class Customer {
 
   private ContactInfo contactInfo;
   ... 
 
}

Marshalling an example Customer would produce the following XML:

<customer>
   <contactInfo 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:type="address">
      <street>323 Main Street</street>
   </contactInfo>
</customer>
 

Note the xsi:type attribute on the contactInfo element.

3.3.2 Using Substitution Groups

Another way to model inheritance in XML is through XML Schema's substitution groups functionality. Using this approach, the element name itself determines which subclass to use.

Taking the same Example 3-12, we will add @XmlRootElement annotations to each of the subclasses, which will act as the inheritance indicator.

Example 3-13 Using @XmlRootElement Annotation

public abstract class ContactInfo {
}
 
@XmlRootElement
public class Address extends ContactInfo {
 
   private String street;
   ... 
 
}
 
@XmlRootElement
public class PhoneNumber extends ContactInfo {
 
   private String number;
   ...
 
}
 

Using this approach, marshalling an example Customer would produce the following XML:

<customer>
   <address>
      <street>323 Main Street</street>
   </address>
</customer>
 

Note that the Address object is marshalled to the address element.

3.3.3 Using @XmlDiscriminatorNode/@XmlDiscriminatorValue

You can also use the MOXY-specific @XmlDiscriminatorNode and @XmlDiscriminatorValue annotations (introduced in EclipseLink 2.2) to represent inheritance. With this approach, you can select the attribute to represent the subtype.

Using Example 3-13, the ContactInfo class uses the @XmlDiscriminatorNode annotation to specify the XML attribute (classifier) that will hold the subclass indicator. Address and PhoneNumber are annotated with @XmlDiscriminatorValue, indicating that class' indicator name (address-classifier and phone-number-classifier).

Example 3-14 Using the @XmlDiscriminatorNode and @XmlDiscriminatorValue Annotations

@XmlDiscriminatorNode("@classifier")
public abstract class ContactInfo {
}
 
@XmlDiscriminatorValue("address-classifier")
public class Address extends ContactInfo {
 
   private String street;
   ... 
 
}
 
@XmlDiscriminatorValue("phone-number-classifier")
public class PhoneNumber extends ContactInfo {
 
   private String number;
   ...
 
}
 

Example 3-14 produces the following XML:

<customer>
   <contactInfo classifier="address-classifier">
      <street>323 Main Street</street>
   </contactInfo>
</customer>

Notice that Address is marshalled to the contactInfo element. Its classifier attribute contains the discriminator node value address-classifier.