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 |