November 4, 2010

JAXB and Inheritance - Using Substitution Groups

In a previous blog post I described how to leverage the xsi:type attribute to represent inheritance.  In this post I'll demonstrate how to use the element name instead by leveraging the XML Schema concept of substitution groups.

Java Model 

The model will contain an abstract super class for all types of contact information.

package blog.inheritance;

public abstract class ContactInfo {

}

Address and PhoneNumber will be the concrete implementations of ContactInfo.  Since we will be using the element name as the inheritance indicator we will annotate each of the sub-classes with @XmlRootElement.

package blog.inheritance;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Address extends ContactInfo {

    private String street;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

}

package blog.inheritance;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PhoneNumber extends ContactInfo {

}

The Customer object can have different types of contact info set on it, so the property will refer to the super class.  We will annotate the contactInfo property with @XmlElementRef to indicate the value type will be derived from the element name (and namespace URI).

package blog.inheritance;

import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private ContactInfo contactInfo;

    @XmlElementRef
    public ContactInfo getContactInfo() {
        return contactInfo;
    }

    public void setContactInfo(ContactInfo contactInfo) {
        this.contactInfo = contactInfo;
    }

}

Demo Code

We will use the following demo code to demonstrate the use of the xsi:type attribute to represent inheritance.

package blog.inheritance;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        Customer customer = new Customer();
        Address address = new Address();
        address.setStreet("1 A Street");
        customer.setContactInfo(address);

        JAXBContext jc = JAXBContext.newInstance(Customer.class, Address.class, PhoneNumber.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

 XML

The following is the resulting XML document.  Note that the Address object is marshalled to the address element.


<customer>
    <address>
        <street>1 A Street</street>
    </address>
</customer>

How does this All Work?

First we will take a look at the schema that represents JAXB's view on this object model.  There is an inheritance among the schema types that matches the inheritance among the Java classes.  Each type has a corresponding global element.  The address and phoneNumber elements specify that they may be substituted for the contactInfo element.

<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="customer" type="customer"/>

    <xs:element name="contactInfo" type="contactInfo"/>

    <xs:element name="address" type="address" 
        substitutionGroup="contactInfo"/>

    <xs:element name="phoneNumber" type="phoneNumber" 
        substitutionGroup="contactInfo"/>

    <xs:complexType name="customer">
        <xs:sequence>
            <xs:element ref="contactInfo"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="contactInfo" abstract="true">
        <xs:sequence/>
    </xs:complexType>

    <xs:complexType name="address">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:sequence>
                    <xs:element name="street" type="xs:string" minOccurs="0"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="phoneNumber">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:sequence/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

Summary

Not all XML binding tools support inheritance.  The ones that do often use different strategies.  Some include the class name of the subclass as the qualifier, this strategy makes it difficult to send the resulting XML document to another tool.  JAXB on the other hand leverages existing XML schema concepts to produce very portable XML documents.  In a future post I'll discuss a MOXy extension for an alternate means of representing inheritance (check out the following for a sneak peak).




Further Reading


If you enjoyed this post you may also be interested in:

19 comments:

  1. Typo! Schema is not valid: the second "address" element declaration should be "phoneNumber"

    ReplyDelete
  2. Hello Sten,

    Thanks for pointing that out, I have made the correction. I hope you found the article useful.

    -Blaise

    ReplyDelete
  3. Hi Blaise. I found this writeup via StackOverflow, and it's helpful except... I want to do this for a parameter on a SOAP method, but @XmlElementRef can't be used in parameters. for example:

    public void setContactInfo(int userID, /* @XmlElementRef */ ContactInfo newInfo) { ... }

    Any suggestions?

    ReplyDelete
  4. Hi Cody,

    Good question, I've tried a couple of different things without much look. You may need to have a method per contact type):
    - public void setAddress(int userID, Address)
    - public void setPhoneNumber(int userID, PhoneNumber)

    You may want to ask this question on the forum below:
    - Java WS & XML Community News

    -Blaise

    ReplyDelete
  5. How do you handle this if Address, PhoneNumber and ContactInfo all need XmlAdapters? XmlJavaTypeAdapter on a class is mutually exclusive with any other annotations.

    For example here, I have a representation of a state machine with all the states, transitions, actions, etc... The parent object has a Set of States, each state has properties common to each typeOfState, and each typeOfState has its own unique properties, some of which can't be directly marshaled.

    How can JAXB handle this?

    ReplyDelete
  6. Gretings, thanks for this very interesting article!
    Using this inheritance principle, I'd like to use it with a list of abstract classes instead of a simple element, such as:

    @XmlRootElement
    public class Customer {

    private List contactInfoList;

    @XmlElementRef
    public List<ContactInfo getContactInfoList() {
    return contactInfoList;
    }

    But somehow if I don't add all possible subclasses in a XmlElementRefs I don't retrieve my objects, so I have to set as:

    @XmlElementRefs({@XmlElementRef(Adress.class),@XmlElementRef(PhoneNumber.class)})

    My goal is to allow ANY subclass of ContactInfo to be set in my list, (and even subclasses which I don't know yet), so how to avoid this classes-list-identified-in-annotation-only limitation?
    Thnaks a lot!

    ReplyDelete
    Replies
    1. I apologize for the delay in getting back to you. The JAXBContext will need to be aware of all the subclasses that you plan to use.

      You could achieve this by using an @XmlSeeAlso({Address.class, PhoneNumber.class}) annotation on the ContactInfo class. Or as in this example you just need to include all the possible subclasses when you bootstrap the JAXBContext.

      -Blaise

      Delete
  7. That's the solution in my example also!
    Thanks for this most elucidating post.

    ReplyDelete
  8. Now unmarshal it...

    javax.xml.bind.UnmarshalException: Unable to create an instance of blog.inheritance.ContactInfo

    ReplyDelete
  9. Wow! Great post! :D
    okay, suppose I have one such collection

    @XmlElementRefs({
    @XmlElementRef(name="ElementA", type=ClassA),
    @XmlElementRef(name="ElementB", type=ClassB) }
    )

    List items;
    Now how do I access every individual element of this list? Is the following code correct?

    for (int j = 0; j < items.size(); ++j) {
    if (items.get(i).getClass().equals(ClassA)) {
    // perform ClassA specific processing:
    } else if (items.get(i).getClass().equals(ClassB)) {
    // perform ClassB specific processing:
    }
    }
    Is this correct approach? Is there a better way to perform each class specific processing? I mean is there a way to avoid those if else constructs?

    ReplyDelete
    Replies
    1. You could use that code. If ClassA and ClassB share a common super type or implement a common interface then you could introduce a common method that both classes implement to do the class specific processing.

      Delete
  10. Hi Blaise,

    I am trying to implement this but on the root Element.
    I have posted my query on the StackOverFlow:
    http://stackoverflow.com/q/17241634/2367540

    Looking forward to hear from you.

    ReplyDelete
  11. Hi Blaise,

    Thank you for the tutorial. However I can't find out how to do when the XML generated needs to have all the same element names? For example in your tutorial:


    1 A Street


    35689457


    instead of having


    1 A Street

    35689457


    In this cas, unmarshalling doesn't work.

    ReplyDelete
    Replies
    1. The XML element names did not survive the constraints of the comment area. However if you don't want to differentiate the subtypes by the element name then you need to use something else as the inheritance indicator. Below are some different options:
      - JAXB and Inheritance - Using the xsi:type Attribute
      - JAXB and Inheritance - MOXy Extension @XmlDescriminatorNode/@XmlDescrimintatorValue
      - JAXB and Inheritance - Using XmlAdapter

      Delete
    2. Thank you very much for your answer which is exactly what I was looking for. A few clarifications for those who, like me, want to use the same element name for all the subtypes :
      - JAXB and Inheritance - Using the xsi:type Attribute, is the basic solution you have probably already found if ou were facing the same problem. The only inconvenience is that this solution adds a namespace tag for each of you subtype which makes your xml not as clear as before
      - JAXB and Inheritance - MOXy Extension @XmlDescriminatorNode/@XmlDescrimintatorValue : Probably a very good solution but you need to use JAXB implemented in the framework EclipseLink instead of the JAXB included in the jdk.
      - JAXB and Inheritance - Using XmlAdapter : Perfect solution for this problem, even if it involves writing a few lines of code, which is maybe not what you want to do when using the JAXB annotations

      Delete
  12. Thank you for clear Explanation

    ReplyDelete

Note: Only a member of this blog may post a comment.