Over a million developers have joined DZone.

JAXB and Inheritance - Using XmlAdapter

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

In previous posts I have covered how to map inheritance relationships in JAXB. This can be done by element name (via @XmlElementRef), by the xsi:type attribute, or in EclipseLink MOXy using another XML attribute (via @XmlDescriminatorNode/@XmlDescriminatorValue).  In this post the type indicator will be an XML attribute/element unique to that type, and we will leverage an XmlAdapter to implement this behaviour.

Input (input.xml)

In this example the possible contact methods are Address and PhoneNumber.  If the street attribute is present on the contact-method element we will instantiate an Address object, and if the number attribute is present we will instantiate a PhoneNumber object.

<?xml version="1.0" encoding="UTF-8"?>
        street="1 A St"
        city = "Any Town"/>

Java Model

Below is the domain model that will be used for this example.


package blog.inheritance.xmladapter;
import java.util.List;
import javax.xml.bind.annotation.*;
public class Customer {
    private List<ContactMethod> contactMethods;


ContactMethod and its subclasses (Address & PhoneNumber) will be handled by an XmlAdapter, so the only mapping required is @XmlJavaTypeAdapter to specify the implementation of XmlAdapter.

package blog.inheritance.xmladapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public abstract class ContactMethod {

package blog.inheritance.xmladapter;
public class Address extends ContactMethod {
    protected String street;
    protected String city;


package blog.inheritance.xmladapter;
public class PhoneNumber extends ContactMethod {
    protected String number;

XmlAdapter (ContactMethodAdapter)

The AdaptedContactMethod class has been created and represents the combined properties of ContactMethod, Address, and PhoneNumber.  In a marshal operation only the properties corresponding to the type being marshalled are populated.  During the unmarshal operation after the AdaptedContactMethod has been built, we can look at which properties have been populated to determine the appropriate subtype that should be returned.

package blog.inheritance.xmladapter;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ContactMethodAdapter extends
    XmlAdapter<ContactMethodAdapter.AdaptedContactMethod, ContactMethod> {
    public AdaptedContactMethod marshal(ContactMethod contactMethod)
        throws Exception {
        if (null == contactMethod) {
            return null;
        AdaptedContactMethod adaptedContactMethod = new AdaptedContactMethod();
        if (contactMethod instanceof Address) {
            Address address = (Address) contactMethod;
            adaptedContactMethod.street = address.street;
            adaptedContactMethod.city = address.city;
        } else {
            PhoneNumber phoneNumber = (PhoneNumber) contactMethod;
            adaptedContactMethod.number = phoneNumber.number;
        return adaptedContactMethod;
    public ContactMethod unmarshal(AdaptedContactMethod adaptedContactMethod)
        throws Exception {
        if (null == adaptedContactMethod) {
            return null;
        if (null != adaptedContactMethod.number) {
            PhoneNumber phoneNumber = new PhoneNumber();
            phoneNumber.number = adaptedContactMethod.number;
            return phoneNumber;
        } else {
            Address address = new Address();
            address.street = adaptedContactMethod.street;
            address.city = adaptedContactMethod.city;
            return address;
    public static class AdaptedContactMethod {
        public String number;
        public String street;
        public String city;
Demo Code

The following demo code will be used for this example.  We will unmarshal the input document, output the type of each object in the collection, and then marshal the objects back to XML.
package blog.inheritance.xmladapter;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
    public static void main(String[] args) throws Exception  {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/blog/inheritance/xmladapter/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);
        for(ContactMethod contactMethod : customer.getContactMethods()) {
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);


The following is the output from running the demo code.  Note how each of the instances of ContactMethod in the collection are of the appropriate sub-type.

class blog.inheritance.xmladapter.PhoneNumber
class blog.inheritance.xmladapter.Address
class blog.inheritance.xmladapter.PhoneNumber
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <contact-method number="555-1111"/>
    <contact-method city="Any Town" street="1 A St"/>
    <contact-method number="555-2222"/>
Related Forum Questions

Below are a couple of use cases that appeared on Stack Overflow that can be implemented using this approach:


From http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!


Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}