| prev | table of contents | next |
The javadoc contained within the classes generated from an XML schema
documents all the getters for accessing an XML element's attributes
and child elements. A good approach is to implement a set of handler
classes, one for each schema element type. Its handle
method retrieves attributes and child elements, for which it invokes the
handle method in turn. This corresponds to a depth-first
traversal of the document tree.The example assumes that there is a simple
set of schema types:
<xsd:complexType name="PersonType">
<xsd:sequence>
<xsd:element name="Name" type="NameType">
<xsd:element name="Addr" type="AddrType" minOccurs="0">
<xsd:element name="Child" type="ChildType"
minOccurs="0" maxOccurs="unbounded">
</xsd:sequence>
<xsd:attribute name="resident" type="xsd:boolean"/>
</xsd:complexType>
<xsd:complexType name="ChildType">
<xsd:complexContent>
<xsd:extension base="PersonType"/>
</xsd:complexContent>
</xsd:complexType>
Below is the essential Java code for a handler class hierachy. Note
that delegation to some handler for a sub-element or attribute
depends on the item having a specific class.
abstract class Handler {
protected static Map<Class<?>,Handler> ourClass2Conv =
new HashMap<Class<?>,Handler>();
static {
ourClass2Conv.put( PersonType.class, new PersonHandler() );
ourClass2Conv.put( NameType.class, new NameHandler() );
ourClass2Conv.put( AddrType.class, new AddrHandler() );
ourClass2Conv.put( ChildType.class, new ChildHandler() );
//...
}
public abstract void handle( Object o );
protected void process( Object obj ){
if( obj != null ){
Handler h = ourClass2Conv.get( obj.getClass() );
if( h != null ){
h.handle( obj );
}
}
}
protected <T> void processList( List<T> list ){
for( T obj: list ){
Handler h = this.getHandler( obj );
h.process( obj );
}
}
}
class PersonHandler extends Handler {
public void handle( Object o ){
PersonType p = (PersonType)o;
process( p.getName() );
if( p.isResident() ){
process( p.getAddr() );
}
processList( p.getChild );
}
}
Not all subclasses of Handler will be quite so simple. There
is one noteworthy complication that arises if subordinate elements have
to be distinguished by their tag. Let's assume a small change in the
definition of PersonType.
<xsd:complexType name="PersonType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="Addr" type="xsd:string" minOccurs="0"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="Boy" type="ChildType"/>
<xsd:element name="Girl" type="ChildType"/>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="resident" type="xsd:boolean"/>
</xsd:complexType>
To get at a person's children, we now have (in class
PersonType) a method getBoyOrGirl(),
that returns a List<JAXBElement<ChildType>>.
All we have to do is a slight extension of the generic method
processList, to access the JAXBElement
object and continue to use its value attribute instead of the
object obtained from the list.
protected <T> void processList( List<T> list ){
for( T obj: list ){
if( obj instanceof JAXBElement ){
obj = ((JAXBElement<?>)obj).getValue();
}
Handler h = this.getHandler( obj );
h.process( obj );
}
}
Finally, if the tag is required for processing as well, the methods
process and handle would have to be extended
by an additional String parameter. The value is obtained
by a call of the JAXBElement method getName().
An additional lookup table mapping tag names to handlers might be
required as well. This is best put into the handler class hosting
the list. Don't make such a map global, because XML tags need not be
unique across the various element types.
| prev | table of contents | next |