Marshalling a List of objects implementing a common interface, with JaxB
For this scenario I would recommend the use of @XmlElements. @XmlElements is used to represent the XML schema concept of choice:
- http://bdoughan.blogspot.com/2010/10/jaxb-and-xsd-choice-xmlelements.html
Here is how it would look for your example:
@XmlElements({
@XmlElement(name="girl", type=Girl.class),
@XmlElement(name="boy", type=Boy.class)
})
@XmlElementWrapper
public List<Person> getPeople() {
return people;
}
@XmlElementRef corresponds to the concept of substitution groups in XML schema. This is why the previous answer requires Person to be changed from an interface to a class.
- http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-substitution.html
OK, if you're prepared to change Person from an interface into an abstract base class, then you're golden. Here's the code:
public class Main {
public static void main(String[] args) throws Exception {
Community community = new Community();
JAXBContext context = JAXBContext.newInstance(Community.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(community, System.out);
}
}
@XmlRootElement(name = "community")
@XmlSeeAlso({Person.class})
public class Community {
private List<Person> people;
@XmlElementWrapper(name="people")
@XmlElementRef()
public List<Person> getPeople() {
return people;
}
public Community() {
people = new ArrayList<Person>();
people.add(new Girl());
people.add(new Boy());
people.add(new Girl());
people.add(new Boy());
}
}
@XmlRootElement(name="boy")
public class Boy extends Person {
public String getName() {
return "John";
}
}
@XmlRootElement(name="girl")
public class Girl extends Person {
public String getName() {
return "Jane";
}
}
@XmlRootElement(name = "person")
@XmlSeeAlso({Girl.class,Boy.class})
public abstract class Person {
@XmlElement(name="name")
public abstract String getName();
}
The main trick was the use of @XmlElementRef in the List of Community. This identifies the type of the class through it's @XmlRootElement. You may also be interested in the @XmlSeeAlso which helps to organise context declarations.
And the output is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<community>
<people>
<girl>
<name>Jane</name>
</girl>
<boy>
<name>John</name>
</boy>
<girl>
<name>Jane</name>
</girl>
<boy>
<name>John</name>
</boy>
</people>
</community>