| prev | table of contents | next |
To define an element type where the element should have a content
consisting of a mixed list of elements use the schema element
xsd:choice with the attribute maxOccurs="unbounded"
or some value greater than 1. (Using xsd:choice as the
sole element within a xsd:sequence would result in the same
structure definition and, therefore, in the same Java code.)
<xsd:complexType name="MixType">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="Text" type="xsd:string"/>
<xsd:element name="Number" type="xsd:int"/>
<xsd:element name="Point" type="PointType"/>
</xsd:choice>
</xsd:complexType>
The generated Java class has an instance variable for a mixed list
of such elements. Obviously, the generic parameter of the List
object must refer to some superclass of all the element types. Sometimes only
java.lang.Object will do. Other possibilities are
java.lang.Serializable, or a user-defined type from which
all the types in the choice set have been derived by subclassing.
public class MixType {
protected List<Object> textOrNumberOrPoint;
public List<Object> getTextOrNumberOrPoint() {
if (textOrNumberOrPoint == null) {
textOrNumberOrPoint = new ArrayList<Object>();
}
return this.textOrNumberOrPoint;
}
}
Note well that this technique does not retain an indication of the
XML tag in the objects created during unmarshalling as long as the
types of the choices are distinct. You must distinguish individual
elements by testing a list element with the instanceof
operator, e.g.:
for( Object o: mix.getTextOrNumberOrPoint() ){
if( o instanceof String ){
// ... cast o to String and process
} else if( o instanceof Integer ){
// ... cast o to Integer and process
} else if( o instanceof Point ){
// ... cast o to Point and process
} else {
throw new IllegalArgumentException( "class " + o.getClass() );
}
}
It's a good idea to use a third test to guard against the class not being
one of the three expected ones. For one thing, the instanceof
test avoids the compiler warning. Also, even though a failure of the cast
would show that something went wrong, we can produce a more significant
error message in the exception we throw in the final else branch.
Writing lengthy if statement cascades like this isn't considered to be good object oriented style. The preferred implementation technique is to map element classes to distinct objects created from subclasses of some element handler class hierarchy. Section Using the Element Tree contains a detailed example illustrating this approach.
If we add another element of, say, type xsd:string,
the schema definition might then look like this:
<xsd:complexType name="Mix4Type">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="Text" type="xsd:string"/>
<xsd:element name="Number" type="xsd:int"/>
<xsd:element name="Point" type="PointType"/>
<xsd:element name="Token" type="xsd:string"/>
</xsd:choice>
</xsd:complexType>
Now the JAXB compiler is forced to use an artificial construct of type
javax.xml.bind.JAXBElement as a container for each of
the elements within MixType. The class Mix4Type
reflects this by the generic list type being
<JAXBElement<?>>. (Notice that the agglomeation of
the list field's name stops, mercifully, after the third sub-element.)
public class Mix4Type {
protected List<JAXBElement<?>> textOrNumberOrPoint;
public List<JAXBElement<?>> getTextOrNumberOrPoint() {
if (textOrNumberOrPoint == null) {
textOrNumberOrPoint = new ArrayList<JAXBElement<?>>();
}
return this.textOrNumberOrPoint;
}
}
Consequently, a pass through the list would have to be changed as
shown below, where the tag and the value are retrieved from the
container of type JAXBElement<?>.
for( JAXBElement> je: mix.getTextOrNumberOrPoint() ){
String tag = je.getName().getLocalPart();
if( "Text".equals( Tag ) ){
String text = (String)je.getValue();
// ... (process)
} else if( "Number".equals( Tag ) ){
Integer number = (Integer)je.getValue();
// ... (process)
} else if( //...
// ... (other alternatives)
}
}
Again, the cascading if statements aren't exactly the bee's knees. See
section Using the Element Tree
for a better way of dealing with tags to distinguish between elements.
| prev | table of contents | next |