Where Did the Abstract Class Come From?
JAXB would generate an abstract class to correspond to an abstract complex type in the XML Schema. In the example below the complex type contactInfo
would correspond to an abstract class called ContactInfo
, also the generated class Customer
would have a property of this type.
<xs:schema
version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.com/foo"
targetNamespace="http://www.example.com/foo">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="contactInfo" type="contactInfo" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="contactInfo" abstract="true">
<xs:sequence/>
</xs:complexType>
<xs:complexType name="address">
<xs:complexContent>
<xs:extension base="contactInfo">
<xs:sequence>
<xs:element name="street" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="phoneNumber">
<xs:complexContent>
<xs:extension base="contactInfo">
<xs:attribute name="number" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
How to Set a Property that is an Abstract Type
For a property of this type you set the value to be one of the concrete subclasses.
Address address = new Address();
customer.setContactInfo(address);
Or
PhoneNumber phoneNumber = new PhoneNumber();
customer.setContactInfo(phoneNumber);
How to Decide Which Subclass to Use?
How do decide which of the concrete subclasses to use?
There isn't really an automated way to do this, you need to make a specific choice based on the data you are trying to represent. In the example above if you want the customer to be contacted by mail you set an instance of Address
otherwise you set an instance of PhoneNumber
.
UPDATE
I did try using one of the concrete subclasses and it works like you
said. The downside is that these concrete subclasses are not really
related to the setter method that I'm using. An example I can think of
is setAppointmentDate(StaffLeaveDates). Here StaffLeaveDates extends
AvailableDates as a concrete subclass but is not really anything to do
with an appointment date but it still works. Do you think this is the
best I can do? I'm just thinking that it could look confusing.
JAXB is going to enable you to create any XML document that is compatible with your XML Schema. As far as it is concerned you told it (via the XML Schema) to allow any type that extends AvailableDates
so it will happily accept StaffLeaveDates
. You can try and change your XML Schema so it is more restrictive (see example below).
XML Schema
Below is an updated version of the XML Schema that introduces a new subtype of contactInfo
called emailAddress
that I don't want to ever be used as a value on the contactInfo
property on customer
. So I have changed customer
to have a choice between address
and phoneNumber
.
<xs:schema
version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.com/foo"
targetNamespace="http://www.example.com/foo">
<xs:element name="customer">
<xs:complexType>
<xs:choice>
<xs:element name="address" type="address"/>
<xs:element name="phoneNumber" type="phoneNumber"/>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:complexType name="contactInfo" abstract="true">
<xs:sequence/>
</xs:complexType>
<xs:complexType name="address">
<xs:complexContent>
<xs:extension base="contactInfo">
<xs:sequence>
<xs:element name="street" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="phoneNumber">
<xs:complexContent>
<xs:extension base="contactInfo">
<xs:attribute name="number" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="emailAddress">
<xs:complexContent>
<xs:extension base="contactInfo">
<xs:attribute name="address" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Generated Model
Now my Customer
class can hold onto an Address
or PhoneNumber
(or both unfortuately), but not an EmailAddress
. Whatever property is null won't be marshalled to XML.
@XmlRootElement(name = "customer")
public class Customer {
protected Address address;
protected PhoneNumber phoneNumber;