Tuesday, 7 August 2012

JAXB Annotation - Can't Handle Interfaces

JAXB Can't Handle Interfaces in Generics

If you have a class with a List<Vehicle> and Vehicle is an interface, JAXB will throw an Exception at runtime:
my.package.MyInterface is an interface,
    and JAXB can't handle interfaces
The solution is to maket the interface an abstract class. For example:
public abstract class Vehicle {

    public abstract String getType();

}

@XmlRootElement
public class Bus extends Vehicle {

    private String type;

    public Bus() { };

    public Bus(String type) {
        this.type = type;
    }

    @XmlElement
    @Override
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

}

@XmlRootElement
public class Car extends Vehicle {

    private String type;

    public Car() {};

    public Car(String type) {
        this.type = type;
    }
 
    @XmlElement
    @Override
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

}

@XmlRootElement
public class ObjectWithListOfVehicles {

    private List<Vehicle> list;

    @XmlElementWrapper(name="MyVehicleList")
    @XmlElement
    public List<Vehicle> getList() {
        return list;
    }

    public void setList(List<Vehicle> list) {
        this.list = list;
    }

}
Assuming the following JAXB context:
public static void interfaceExamples() throws JAXBException {

    List<Vehicle> l = new ArrayList<Vehicle>();
    l.add(new Bus("Large bus"));
    l.add(new Bus("Small bus"));
    l.add(new Car("Ferrari"));

    // Object with generic list
    ObjectWithListOfVehicles owgl
        = new ObjectWithListOfVehicles();
    owgl.setList(l);

    JAXBContext jc = JAXBContext.newInstance(
        Bus.class, Car.class,
        ObjectWithListOfVehicles.class);

    ObjectWithListOfVehicles retr
        = marshallUnmarshall(owgl, jc);

    for (Vehicle s : retr.getList()) {
        System.out.println(
            s.getClass().getSimpleName()
            + " - " + s.getType());
    } System.out.println(" ");

}

public static <O> O marshallUnmarshall(O o, JAXBContext jc)
        throws JAXBException {

    // Creating a Marshaller
    Marshaller jaxbMarshaller = jc.createMarshaller();
    jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    StringWriter result = new StringWriter();
    jaxbMarshaller.marshal(o, result);

    // Printing XML
    String xml = result.toString();
    System.out.println(xml);

    // Creating an Unmarshaller
    Unmarshaller jaxbUnmarshaller = jc.createUnmarshaller();
    StringReader sr = new StringReader(xml);

    O retr = (O) jaxbUnmarshaller.unmarshal(sr);

    return retr;

}
The generated XML and verification is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithListOfVehicles>
    <MyVehicleList>
        <list xsi:type="bus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Large bus</type>
        </list>
        <list xsi:type="bus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Small bus</type>
        </list>
        <list xsi:type="car" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Ferrari</type>
        </list>
    </MyVehicleList>
</objectWithListOfVehicles>

Bus - Large bus
Bus - Small bus
Car - Ferrari

Notice that JAXB is capable of recreating both Bus and Car instances.

Next

JAXB to XMLJAXB to JSONJAXB Annotations Tutorial Table Of Content

2 comments:

  1. Hi,

    Nice article. Here is an alternate approach that you can use with JAXB implementation when you are working with interfaces:
    - JAXB and Interface Fronted Models

    -Blaise

    ReplyDelete
  2. Hi,
    Really very very nice article about JAXB...

    ReplyDelete