I have an xml like this:
<todo>
<doLaundry cost="1"/>
<washCar cost="10"/>
<tidyBedroom cost="0" experiencePoints="5000"/>
</todo>
And the XSD schema for it is:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="todo">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element name="doLaundry" type="doLaundry" />
<xs:element name="washCar" type="washCar" />
<xs:element name="tidyBedroom" type="tidyBedroom" />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="doLaundry">
<xs:attribute name="cost" type="xs:int" />
</xs:complexType>
<xs:complexType name="washCar">
<xs:attribute name="cost" type="xs:int" />
</xs:complexType>
<xs:complexType name="tidyBedroom">
<xs:attribute name="cost" type="xs:int" />
<xs:attribute name="experiencePoints" type="xs:int" />
</xs:complexType>
</xs:schema>
And when I process this schema through JAXB I get a class with a method like this:
public class Todo {
public List<Object> getDoLaundryOrWashCarOrTidyBedroom() {
...
}
}
Ideally, what I would like is a way to define a generic base type that all the other XSD types extend. The Jaxb classes generated from the XSD schema should have a method to return a list of generic tasks. This would make it very easy to add new tasks to the todo list:
public class Todo {
public List<Task> getTasks() {
...
}
}
public abstract class Task {
public int getCost() {
...
}
}
public class TidyBedroom extends Task {
public int getExperiencePoints() {
...
}
}
What should the XSD schema look like in order to generate the above Java classes?
I found the answer with the help of Blaise Doughan's article here: http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-xsitype.html
This schema:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="todo"> <xs:sequence> <xs:choice maxOccurs="unbounded"> <xs:element name="doLaundry" type="doLaundry" /> <xs:element name="washCar" type="washCar" /> <xs:element name="tidyBedroom" type="tidyBedroom" /> </xs:choice> </xs:sequence> </xs:complexType> <xs:complexType abstract="true" name="Task"> <xs:attribute name="cost" type="xs:int" use="required" /> </xs:complexType> <xs:complexType name="doLaundry"> <xs:complexContent> <xs:extension base="Task"> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="washCar"> <xs:complexContent> <xs:extension base="Task"> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="tidyBedroom"> <xs:complexContent> <xs:extension base="Task"> <xs:attribute name="experiencePoints" type="xs:int" /> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>
combined with a binding file:
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jxb:bindings> <jxb:bindings schemaLocation="todo.xsd" node="/xs:schema/xs:complexType[@name='todo']/xs:sequence/xs:choice"> <jxb:property name="Tasks"/> </jxb:bindings> </jxb:bindings> </jxb:bindings>
Will give abstract and inherited classes as I described in the question. The binding file will change Jaxb's default method name from getDoLaundryOrWashCarOrTidyBedroom() to getTasks().
xsd:choice corresponds to the @XmlElements annotation. You could apply this annotation directly to your desired object model.
For more information see:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With