XmlDiscriminatorNode/XmlDiscriminatorValue with multiple levels of inheritance
Question
I have the following domain model:
@XmlDiscriminatorNode("@type")
class Vehicle
@XmlDiscriminatorValue("car")
class Car extends Vehicle
@XmlDiscriminatorValue("tank")
class Tank extends Vehicle
@XmlDiscriminatorValue("sedan")
class Sedan extends Car
In my top level model class I have a List<Vehicle>
that I want to serialize. I want to serialize this model with moxy so that the fields in Sedan show up. When I add XmlDiscriminatorNode
to Vehicle and add XmlDiscriminatorValue
to the extension classes, only the first level of inheritance is serialized. In the above example, the attributes of Car show up but not the attributes of Sedan. How do I annotate these classes so that Sedan's attributes show up?
Solution
When JAXB derives metadata for a model transitive relationships are processed. When a class is processed its super classes are also processed, but not its subclasses. To overcome this you will need to make JAXB aware of the subclasses. One way to do this is to use the @XmlSeeAlso
annotation.
Java Model
I will use the Java model from your question with the following changes:
Vehicle
I have added the @XmlSeeAlso
annotation. Only the leaf classes need to be added, as processing Sedan
will cause the Car
class to be processed.
import javax.xml.bind.annotation.XmlSeeAlso;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
@XmlDiscriminatorNode("@type")
@XmlSeeAlso({Tank.class, Sedan.class})
class Vehicle {}
Sedan
I have added a property to the Sedan
class to show that it appears in the marshalled result.
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
@XmlDiscriminatorValue("sedan")
class Sedan extends Car {
private String sedanProperty;
public String getSedanProperty() {
return sedanProperty;
}
public void setSedanProperty(String sedanProperty) {
this.sedanProperty = sedanProperty;
}
}
Foo
Here is a root class as you describe in your question that has a List
of Vehicle
objects.
import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Foo {
private List<Vehicle> vehicles = new ArrayList<Vehicle>();
@XmlElement(name="vehicle")
public List<Vehicle> getVehicles() {
return vehicles;
}
}
Demo Code
Demo
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Sedan sedan = new Sedan();
sedan.setSedanProperty("Sedan Value");
Foo foo = new Foo();
foo.getVehicles().add(sedan);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<vehicle type="sedan">
<sedanProperty>Sedan Value</sedanProperty>
</vehicle>
</foo>