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 |