XML
Object Binder
User
Guide
Doc
Version 1.6
XOB
Version 3.1
http://xob.sourceforge.net/
Introduction 3
What
is the point of this ? 3
Terms 3
What
XOB does 3
Dependencies 5
Apache
Xerces parser 5
Jar
files 5
Licence 5
Using 6
Simple
example XML and interfaces 6
Reading
XML (unmarshalling) 7
DTD
validation 7
Schema
validation 8
Filtering 8
Creating
XML objects 9
Namespace 10
Writing
XML (marshalling) 11
DTD 11
Schema 11
Validating
XML Object structure 11
Using
the interface generator (XOBGen) 12
Example
java -jar usage 13
Example
Ant task usage 14
Special
features 14
Using
the maven2 plugin 15
Using
the schema HTML doucmentation generator (XSDDoc) 16
Example
java -jar usage 16
Example
Ant task usage 17
Writing
interfaces 18
Interface
names 18
Tags 18
Child
tags 18
Namespace 19
Tag
namespace and naming 19
Attributes 19
Attribute
namespace 20
Tag
value 20
Examples 20
Tested
XML parsers 20
Configuration 20
Author/contact 21
Introduction
XOB is short for XML Object Binder which lets you
map interfaces to XML tags in XML documents and have them
automatically implemented on the fly when unmarshalling an XML file
into objects represented by your interfaces. XOB also allows you to
create interface implementations through a factory interface, set
values through the interfaces and then marshal into an XML file. This
might sound a bit cryptic, but it will become clear if you continue
to read this document.
The examples catalog contains enough examples to
get you going if you hate reading documentation. Do however read the
“Examples” section at the end of this document first if you want
to run the examples.
What is the point of this ?
When reading an XML file using the JAXP and DOM
APIs directly all XML file elements and attributes are referenced by
a string (ex: document.getElement(“orderDate”);). If you misspell
this string reference (which is easily done) the result is a null
value returned at runtime. You can't easily see what valid references
are available in each getElement() when you are coding.
When an XML element is bound to a Java object
there is a specific getter for each subelement and attribute. Any
misspelling is cauth by the compiler at compiletime. In most IDEs
entering the name of the bound Java XML object and then a dot will
give you a menu of valid choices.
When used for creating an XML file it is much
harder to produce an incorrect XML file (though still possible –
with effort :-). It is impossible to set an incorrect attribute. It
is impossible to set an incorrect subelement. It is however possible
to add an incorrect nubmer of subelements.
In general working with a Java object hierarchy is
much easier than using something like the DOM API.
Terms
XML Object – A java object representing an XML
element, defined by an interface, and implemented by XOB.
What XOB does
Unmarshals any XML InputStream into java “XML
objects”.
Doing no validation.
Doing DTD validation.
Doing Schema validation.
Creates “XML objects”.
Validate “XML objects”.
Marshals “XML object” as XML to any
OutputStream.
Generates “XML Object” interfaces from
XML Schema files.
Generates HTML documentation for XML Schema
files.
Gives you control over the naming of the XML
objects, which doesn't have to be identical to its XML element.
Allows you to filter XML data when accessing
XML objects.
Allows you to override some schema values
using comments when generating XOB interfaces from an XML schema.
This is useful in conjunction with filters. A returns many can for
example be overridden to a returns one when that is the case when a
filter is applied.
Dependencies
XOB requires an XML parser, preferably one that
supports JAXP 1.1 or JAXP 1.2. It is possible to use other parsers,
but then you have to implement a wrapper for the non JAXP parser that
implements the xob.xml.XMLParser interface, and set that parser on
the XMLObjectBinder instance.
If you use a JAXP parser you must have one that
implements JAXP 1.2 to be able to use schema validation.
To use a JAXP parser, just make it available in
the CLASSPATH. The default internal XMLParser implementation uses the
javax.xml.parsers.* APIs.
Apache Xerces parser
The XOB distribution includes the Xerces parser
from Apache (http://xml.apache.org/xerces2-j/index.html).
It is used for building and for some of the examples.
Jar files
The XOB binary distribution contains the following
4 jar files:
xob.jar
|
This is the main XOB implementation. This jar
file is required runtime if you are going to use XOB! This is ALL
that is needed runtime! Just drop this jar into .../lib/ext or
somewhere in your project and add it to the classpath.
|
xobxsd.jar
|
This jar file contains common XML schema code
used by both xobgen.jar and xsddoc.jar.
|
xobgen.jar
|
This generates XOB interfaces from an XML
schema file. This jar is executable with “java -jar xobgen.jar”.
It also contains an Ant task for use in Ant build scripts.
|
xsddoc.jar
|
This generates HTML documentation for XML
schemas. This jar is executable with “java -jar xsddoc.jar”.
It also contains an Ant task for use in Ant build scripts.
|
xob-m2-plugin.jar
|
Since version 3.1 XOB is now built with maven2.
This jar is a maven2 plugin for running xobgen in a simple way.
The xobgen Ant task can also be used with maven2 via the antrun
plugin, but having a real maven plugin is both simpler and more
flexible.
|
Licence
From version 3.1 this code is released under the
Apache Software License 2.0, which is included as a text file in the
binary distribution and also available at
http://www.apache.org/licenses/LICENSE-2.0.
Using
Using XOB is really simple! To use XOB you begin
by creating interfaces that maps to the tags in the XML document you
are going to read/write/create. See the “Writing interfaces”
section below for a description on rules for the interfaces.
Alternatively if your have an XML Schema or create one you can use
xobgen.jar to generate the interfaces. See below for more information
on using the generator.
Simple example XML and interfaces
Take the following XML file:
<purchaseOrder shipped="false">
<orderDate>2003-04-22</orderDate>
<productId>832684</productId>
<productId>346734</productId>
<customerId>2674346</customerId>
<comment>Delayed delivery</comment>
</purchaseOrder>
Then you create the following interfaces:
public interface PurchaseOrder extends XMLObject {
public boolean getShipped();
public void setShipped(boolean shipped);
public OrderDate getOrderDate();
public void setOrderDate(OrderDate orderDate);
public Iterator getProductIds();
public void addProductId(ProductId id);
public void addProductIdGrouped(ProductId id);
public CustomerId getCustomerId();
public void setCustomerId(CustomerId id);
public Comment getComment();
public void setComment(Comment comment);
}
public interface OrderDate extends XMLObject {
public xob.xml.datatypes.XSDDate getOrderDateValue();
public void setOrderDateValue(xob.xml.datatypes.XSDDate orderDateValue);
}
public interface ProductId extends XMLObject {
public int getProductIdValue();
public void setProductIdValue(int value);
}
public interface CustomerId extends XMLObject {
public int getCustomerIdValue();
public void setCustomerIdValue(int value);
}
public interface Comment extends XMLObject {
public String getCommentValue();
public void setCommentValue(String value);
}
Reading XML
(unmarshalling)
To read the above example XML file using the
example interfaces you would do:
import java.io.FileInputStream;
import xob.XOBParseException;
import xob.Factory;
import xob.XMLObjectBinder;
import xob.XMLUnmarshaller;
public class PurchaseOrderReaderExample {
public static void main(String[] args) throws Exception {
XMLObjectBinder binder =
Factory.createXMLObjectBinder(PurchaseOrder.class);
binder.getXMLParser().setValidating(false); // We dont have a schema or DTD!
try {
XMLUnmarshaller unmarshaller = binder.createUnmarshaller();
PurchaseOrder po = (PurchaseOrder)unmarshaller.unmarshal(
new FileInputStream(args[0])
);
// The following will not throw any exception! We place it in the try
// block just so that it only executes after a successful unmarshal.
System.out.println("Has been shipped:" + po.getShipped());
System.out.println("Order date: " + po.getOrderDate().getOrderDateValue());
for (Iterator it = po.getProductIds(); it.hasNext();) {
ProductId prodId = (ProductId)it.next();
System.out.println("ProductId:" + prodId.getProductIdValue());
}
System.out.println("CustomerId:" + po.getCustomerId().getCustomerIdValue());
System.out.println("Comment:" + po.getComment().getCommentValue());
}
catch (XOBParseException xpe) {
System.out.println("Failed to parse " + args[0] + " (" +
xpe.getMessage() + ")");
}
}
}
DTD validation
To do DTD validation you manipulate the
XMLUnmarshaller object before the unmarshal() call:
unmarshaller.setValidating(true);
unmarshaller.setSchemaValidating(false);
Or the binder before creating the unmarshaller:
binder.getXMLParser().setValidating(true);
binder.getXMLParser().setSchemaValidating(false);
Making the XMLUnmarshaller validating, and setting schema validation
to false, implies DTD validation. After the unmarshal() call you can
also do:
xob.xml.DTDRef dtdRef = unmarshaller.getDTDRef()
To get the parsed DTD reference. If you are going to rewrite the XML
file again you need to save this DTDRef and set it on the
XMLMarshaller before calling the marshal() method. DTD references are
not retained if you simply unmarshal and then marshal again.
Schema
validation
By now I'm sure you can guess that for schema
validation you do:
unmarshaller.setValidation(true);
unmarshaller.setSchemaValidation(true);
Or
binder.getXMLParser().setValidation(true);
binder.getXMLParser().setSchemaValidation(true);
before creating the unmarshaller.
And after the unmarshal() call you can do:
xob.xml.SchemaRef schemaRef = unmarshaller.getSchemaRef()
to get the schema reference. There are 2 ways to access and set
schema references. One is by using the SchemaRef object. The other is
by simply defining getters/setters for the schema attributes in your
top level interface.
Filtering
It is possbile to filter the XML data when getting
it using the XOB interfaces. Filters are applied to a binder before
unmarshalling, the whole file are however parsed and loaded so
remarshalling will not loose any data.
To use filtering you must first create a
filterset. This is done using the binder instance:
XMLDataFilterSet filterSet = binder.createXMLDataFilterSet();
Next you set an attribute name that specifies the
name of a filter to apply to subtags of a tag:
filterSet.setFilterNameAttribute(“filterName”);
In this example, if a tag in the input has a
filterName=”myfilter” attribute a filter named “myfilter”
will be applied to subtags of the tag if it is defined (continue
reading!).
Next you use the filterset to create a filter:
XMLDataFilter myFilter = filterSet.makeFilter(“myfilter”);
Then you configure the filter by specifying an
attribute to filter and a value the attribute must have to be
accepted:
myFilter.setFilterAttribute(“access”);
myFilter.setFilterValue(“public”);
Lastly you set the filterSet on the unmarshaller:
unmarshaller.setXMLDataFilterSet(filterSet);
Then the XML file read might look like this:
...
<... filterName=”myfilter”>
<... access=”public”/>
<... access=”public”/>
<... access=”private”/>
<... access=”public”/>
</...>
...
The getter of the XOB interface representing the
tag containing filterName=”myfilter” for getting the subtags will
only return those that contain access=”public”.
Creating XML objects
If you first unmarshal an XML file then make
changes through the interfaces and then marshal again, the interface
implementations have been provided by the unmarshal. But if you want
to create a new XML file from scratch, how do you get an
implementation of your interfaces so that you can set information on
them and then marshal? The answer is: a factory interface!
To create objects implementing your interfaces you
have to create a factory interface for creating them. xobgen.jar
(explained below) also generates a factory interface unless you
specify the readonly flag. Here is an example factory for the above
example:
public interface PurchaseOrderFactory {
PurchaseOrder createPurchaseOrder() throws Exception;
OrderDate createOrderDate() throws Exception;
CustomerId createCustomerId() throws Exception;
ProductId createProductId() throws Exception;
Comment createComment() throws Exception;
}
As you can see, all methods start with “create” and then the name
of the interface they return an implementation of. The return type is
of course the same interface. Make all create methods throw
Exception. There are mostly reflection exceptions that can be thrown
by the methods, but if they fail, they fail. It is not really
important exactly what failed when catching these exceptions. You can
always dump the exception to find out what really happened if you
want, but from a program logic perspective just throwing and catching
Exception is good enough in this case.
This interface class is then passed as a second
argument to the createXMLObjectBinder() factory method. Ex:
XMLObjectBinder binder =
Factory.createXMLObjectBinder(PurchaseOrder.class,
PurchaseOrderFactory.class);
You get an implementation of the factory interface by calling the
getFactoryInstance() method on the binder instance.
Example:
PurchaseOrderFactory pof = (PurchaseOrderFactory)binder.getFactoryInstance();
PurchaseOrder purchaseOrder = pof.createPurchaseOrder();
purshaseOrder.setShipped(false);
OrderDate orderDate = pof.createOrderDate();
orderDate.setOrderDateValue(new xob.xml.datatypes.XSDDateTime("2003-05-02T12:20:05"));
purshaseOrder.setOrderDate(orderDate);
ProductId prodId = pof.createProductId();
prodId.setProductIdValue(9213465);
purshaseOrder.addProductId(prodId);
...
Namespace
If the objects you are creating needs to be
prefixed with a namespace prefix you need to use a third version of
the createXMLObjectBinder() method. Say for example that your top
element have something like this:
xmlns:nse=”http://my.site.com/mynamespace/NameSpaceExample”
and the schema contains the following attribute:
elementFormDefault=”qualified”
Then if you create your binder like this:
XMLObjectBinder binder =
Factory.createXMLObjectBinder(PurchaseOrder.class,
PurchaseOrderFactory.class,
“nse”);
Then any object created by the factory returned by
“binder.getFactoryInstance()” will have the “nse” namespace
prefix. You also need to bind the prefix with the namespace. There
are 2 ways to do that. If you manually created your interfaces you
could add the following 3 methods to the top level interfaces:
public void setXmlns_Nse(String nseNamespace);
public void setXmlns_Xsi(String xsiNamespace);
public void setXsi_SchemaLocation(String schemaLocation);
You can then do:
myobj.setXmlns_Nse(“http://my.site.com/mynamespace/NameSpaceExample”);
myobj.setXmlns_Xsi(“http://www.w3.org/2001/XMLSchema-instance”);
myobj.setXsi_SchemaLoation(“http://my.site.com/mynamespace/NameSpaceExample myxsd.xsd”);
A Simpler alternative is to create a SchemaRef object and set it on
the marshaller. You have to use this alternative if you have
generated your interfaces with xobgen.jar. See Writing XML/Schema
below for an example.
Writing XML
(marshalling)
To write XML objects you simply create an
XMLMarshaller from the binder and then call the marshal() method.
Example:
XMLMarshaller marshaller = binder.createMarshaller();
marshaller.marshal(po, System.out);
This example writes po, which is a PurchaseOrder instance, with all
of its children to the System.out stream as XML.
DTD
To add a DTD reference to the written XML, create
a DTDRef object using the factory methods in XMLObjectBinder or get
it from an XMLUnmarshaller after unmarshalling and set it on the
XMLMarshaller instance before calling the marshal() method.
Example:
XMLMarshaller marshaller = binder.createMarshaller();
DTDRef dtdRef = binder.createDTDRef(mySystemId, myPublicId);
marshaller.setDTDRef(dtdRef);
marshaller.marshal(po, System.out);
Schema
If you unmarshal, change and then marshal again,
the schema references will be retained. If you are creating a new XML
document from scratch you can either define setters for the schema
attributes in your top level interface or use the createSchemaRef()
factory method in XMLObjectBinder to create a new SchemaRef and then
set it on the XMLMarshaller instance before calling the unmarshal()
method.
Example:
XMLMarshaller marshaller = binder.createMarshaller();
SchemaRef schemaRef = binder.createSchemaRef(“http://my.site.com/mynamespcace/NameSpaceExample”,
“nse”,
“myxsd.xsd”);
marshaller.setSchemaRef(schemaRef);
marshaller.marshal(po, System.out);
Validating XML Object structure
You might want to validate your XML object
structure before you write it. This can be done with the
XMLValidator. Just like the XMLUnmarshaller and XMLMarshaller it is
created by the XMLObjectBinder instance.
Example:
PurchaseOrder po ...
...
XMLValidator validator = binder.createValidator();
validator.setSchemaRef(schemaRef);
try {
validator.validate(po);
}
catch (XOBValidateException xve) {
System.out.println(“Validation failed:”);
System.out.println(xve.getMessage()); // Will list all errors.
}
The example does setSchemaRef() on the validator. You need to set
either a SchemaRef or a DTDRef depending on if you validate against a
schema or dtd.
Using the interface generator (XOBGen)
If you have an XML Schema describing your xml file
then the quickest and simplest is to use the interface generator. It
resides in xobgen.jar and can be run with “java -jar xobgen.jar
...” or using the xob.xobgen.XOBGenAntTask Ant task which also
resides in xobgen.jar.
The generator and the Ant task have the following
arguments:
Option
|
Argument
|
Description
|
readonly
|
true/false
|
Only generates getters and no factory if
true.
|
outputdir
|
dir
|
Where the interfaces should be generated. If
the package argument is also specified then the resulting
package path is added to this, so this becomes the package root
dir.
|
package
|
package
|
The java package the generated interfaces
should belong to.
|
verbose
|
true/false
|
If true then the files being generated are
listed on standard out.
|
xsd
|
Schema file
|
The XML schema file to generate from.
|
map
|
elementName objectName
|
This maps the specified element name to the
specified object name. If this is specified for an element name
the generator will not try to convert the element name to an
object name itself but will use the specified object name
instead. This option can be specified any number of times.
If run with java -jar there should be a space
between the element name and the object name. If run with the
Ant task this is specified with
<map elementName=”name” objectName=”name”/>
subtags.
Any mapped elements will have a:
public static final String XOM_objectName = “elementName”;
generated in the interface. This is how XOB finds the real
element name for the object.
|
automap
|
true/false
|
If true A map is automatically created for
all elements not mapped specifically with map. Automap maps to
the exact name specified by the schema.
There is a good reason for this due to how
XOB works: For any non mapped element XOB uses the name of the
getter/setter to determine the element name. A getter/setter has
the first character after the word get/set capitalized!
Therefore XOB makes a possibly incorrect assumption that the
element name does not start with a capital letter. When reading
XML it could try both and see what it finds, but when writing it
has no way of knowing, making some XML files readable but not
writable, if XOB were to do that. I did not like that solution.
This problem however completely goes away
when you have a map! In the map case XOB knows the element name
exactly and does not have to construct it.
I'm actually considering making this default
to true in the future.
|
When run with “java -jar” the options should
be prefixed with “--” and true/false options take no argument,
just specifying the options makes them true (which means that they
default to false!).
When run through the Ant task the options are
attributes and true/false options must be specified as either “true”
or “false”. The Ant task also supports the <fileset>
subtask as an alternative to xsd=”file.xsd”. This also allows for
generating for more than one input file in one run which the java
-jar version does not.
If
<xs:annotation><xs:documentation>bla</xs:documentation></xs:annotation>
tags are found when parsing the schema they will be used as javadoc
comments for the generated interfaces. Any “[“ will be translated
to “<”, any “]” will be translated to “>”, any “[[“
will be translated to “<” and any “]]” will be
translated to “>”. This makes it rather simple to put html
tags in the documentation without using CDATA.
Example java -jar usage
java
-jar .../xob/jars/xobgen.jar –-verbose –-readonly –-package
“my.pkg” --outputdir “src” –-xsd MySchema.xsd
Example Ant
task usage
<target name="def-xobgen" depends="init" description="Depend on this if you are going to use xobgen.">
<taskdef name="xobgen" classname="xob.xobgen.XOBGenAntTask[Real]">
<classpath>
<pathelement location="${xob-dir}/jars/xob.jar"/>
<pathelement location="${xob-dir}/jars/xobxsd.jar"/>
<pathelement location="${xob-dir}/jars/xobgen.jar"/>
<!-- Using the xerces jars from the xob dist. -->
<!-- These are not needed if you run jdk 1.4! -->
<pathelement location="${xob-dir}/extjars/xercesImpl.jar}"/>
<pathelement location="${xob-dir}/extjars/xmlParserAPIs.jar"/>
<pathelement location="${xob-dir]/extjars/xml-apis.jar"/>
</classpath>
</taskdef>
</target>
<target name=”generateIfs” depends=”init,def-xobgen”>
<xobgen verbose="true" outputdir="../src/runtime" package=”my.pkg” xsd="PurchaseOrder.xsd"/>
<!-- This is probably not very useful, but possible. -->
<xobgen verbose=”true” outputdir=”../src/prod” package=”my.pkg.xml”>
<fileset dir=”../src/prod”>
<include name=”**/*.xsd”/>
</fileset>
</xobgen>
</target>
Please note that you should use classname=”xob.xobgen.XOBGenAntTask”
if you are using Ant 1.5.4 or below. This Ant task wraps
XOBGenAntTaskReal which is loaded using an XOB internal class loader
to go around a bug in Ants class loader for these versions. If you
are using Ant 1.6.0 or above you should use
classname=”xob.xobgen.XOBGenAntTaskReal” directly since the Ant
classloader bug has been corrected, and the XOB internal class loader
has a problem with these versions of Ant.
Special features
You can affect the multiplicity of an element
through a comment in the XML schema. Example:
<xsd:element name="officeCity">
<xsd:complexType>
<xsd:sequence>
<!-- XOB:Generator-override maxOccurs="1" -->
<xsd:element ref="city" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="filterName" use="optional" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
In this case the XML schema says that there are one to many city
elements under the officeCity element. The
“<!--XOB:Generator-override maxOccurs=”1” -->” comment
however tells the generator to see this as only one city element
being available under officeCity. This will generate a getter that
returns a City object instead of an Iterator.
Now you are probably wondering “Why?”. This
might seen quite an odd thing to do, but this works well in
conjunction with filters as the example above implies. If you apply a
filter that will always only return one sub-element then you can make
the generated API reflect this.
Using the
maven2 plugin
The maven2 plugin for xobgen works a bit different
than the Ant task. To start with you need to add the following plugin
declaration to your pom.xml:
<plugins>
<plugin>
<groupId>net.sf.xob</groupId>
<artifactId>xob-m2-plugin</artifactId>
<executions>
<execution>
<id>generate-xob-apis</id>
<configuration>
<scanDirs>src/main/resources</scanDirs>
<outputRelativeDir>src/main/java</outputRelativeDir>
<schemaExt>.xsd</schemaExt>
<readOnly>true</readOnly>
<!--map>elementName=objectName,...</map-->
<verbose>true</verbose>
</configuration>
<goals>
<goal>genapi</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
</plugins>
This configures the plugin to run during the generate-sources phase.
The “scanDirs” configuration can actually take a comma separated
list of paths.
Now you might think that there is something
missing, like what schemas to generate from and what package to
generate to for each schema. This is where “different” comes in.
The plugin scans the filessystem threes starting at the specified
“scanDirs” roots. Any file with the “schemaExt” extension
will be opened and the first 10 lines scanned for a “@xobgen
<package>” specification, preferably in a comment if you want
the schema to be parsable. The <package> part is the package of
the generated API. When the plugin sees this is will add the file to
the list of schemas to generate APIs for. When the scanning is done
the found schemas will have java APIs generated for them at
“outputRelativeDir” plus path of specified package.
Using the
schema HTML doucmentation generator (XSDDoc)
Since the interface generator resulted in an xsd
parser that also extracts documentation I realized that it would be
nice and rather simple to also make a HTML documentation generator
for a schema. The doc generator resides in xsddoc.jar and can be run
with “java -jar xsddoc.jar ...” or with the
xob.xsddoc.XSDDocAntTask Ant task also residing in xsddoc.jar.
The generator and the Ant task have the following
options:
Option
|
Argument
|
Description
|
alt
|
true/false
|
Normally the generated documentation consists
of 4 sections: complexTypes, groups, attributeGroups, and
elements. Attributes and simple types are expanded into those
objects that references them. Specifying true for alt generates
an alternative output with only elements and everything expanded
into the elements. This view can be nicer since for each element
you can very easily see all subelements and attributes allowed.
The normal view more reflects the schema structure.
|
verbose
|
true/false
|
If true files are listed as they are being doc
generated.
|
docdir
|
dir
|
The directory where the HTML docs will be
written.
|
When run with “java -jar” all options are
prefixed with “--”. True/false type options have no argument,
just specifying them makes them true. The input files to genereate
docs for are specified after all options. You can specify more than
one file.
When run through the Ant task the options are
attributes. True/false type options must be specified as either
“true” or “false”. All input files are specified using a
<fileset> subtask.
There are 2 extra files written to the docdir
catalog: xsddocIndex.html and XSDIndex.html. The xsddocIndex.html is
a frames document that shows XSDIndex.html in a small frame to the
left and the first generated document in the doc frame to the right.
XSDIndex.html contains links to all documents generated in the same
run. Clicking on them will display them in the right frame.
For any
<xs:annotation><xs:documentation>bla</xs:documentation></xs:annotation>
any “[“ will be translated to “<”, any “]” will be
translated to “>”, any “[[“ will be translated to “<”
and any “]]” will be translated to “>”. This makes it
rather simple to put html tags in the documentation without using
CDATA. For the <xs:schema> tag docs you can use @version and
@author tags also, like javadoc. They can actually be used in any
documentation block, but they make most sense for the schema
documentation.
Example java -jar usage
java -jar xob/jars/xsddoc.jar –-verbose –-alt –-docdir “schemaDocs” myschema.xsd myotherschema.xsd
Example Ant task usage
<target name="def-xsddoc" depends="init" description="Depend on this if you are going to use xsddoc.">
<taskdef name="xsddoc" classname="xob.xsddoc.XSDDocAntTask[Real]">
<classpath>
<pathelement location="${xob-dir}/jars/xob.jar"/>
<pathelement location="${xob-dir}/jars/xobxsd.jar"/>
<pathelement location="${xob-dir}/jars/xsddoc.jar"/>
<!-- Using the xerces jars from the xob dist. -->
<!-- These are not needed if you run jdk 1.4! -->
<pathelement location="${xob-dir}/extjars/xercesImpl.jar}"/>
<pathelement location="${xob-dir}/extjars/xmlParserAPIs.jar"/>
<pathelement location="${xob-dir]/extjars/xml-apis.jar"/>
</classpath>
</taskdef>
</target>
<target name=”generateDocs” depends=”init,def-xsddoc”>
<xsddoc verbose=”true” docdir=”../schemaDocs” alt=”true”>
<fileset dir=”../resources/”>
<include name=”**/*.xsd”/>
</fileset>
</xsddoc>
</target>
Please note that you should use classname=”xob.xsddoc.XSDDocAntTask”
if you are using Ant 1.5.4 or below. This Ant task wraps
XSDDocAntTaskReal which is loaded using an XOB internal class loader
to go around a bug in Ants class loader for these versions. If you
are using Ant 1.6.0 or above you should use
classname=”xob.xsddoc.XSDDocAntTaskReal” directly since the Ant
classloader bug has been corrected, and the XOB internal class loader
has a problem with these versions of Ant.
Writing
interfaces
If you are going to write your own interfaces
instead of generating them, which you will have to do if you don't
have a schema or want to make one, this section explains how.
Interface names
The name of the interfaces should match the name
of the element they represent, but with the first charater
capitalized. Any '.' in an elment name must be replaced with “
dot “
(underscore-”dot”-underscore). Any '-' in an element name must be
replaced with “ dash
“
(underscore-”dash”-underscore). If you dont like these type of
annoying names then call your interface anything you want and then
define the following constant in it:
public static final String XOM_interfaceName = “elementName”;
where interfaceName is
whatever you choose to call your inteface, and elementName is
the real name of the element this interface represents.
Tags
An interface represents an XML tag. It should as
minimum contain getters for the child tags and attributes wanted. If
you want to use these interfaces for createing an XML file then you
should also add setters to the interfaces.
All interfaces must extend the
xob.XMLObject interface!
Child tags
The getter for a child tag that returns zero or
one child tag object should look like this:
public childtype getchildtype();
where childtype is exactly the name of the interface
representing the child tag.
The getter for a child tag that returns zero or
more child tag objects should look like this:
public Iterator getchildtypes();
where childtype is exactly the name of the
interface representing the child tag, and is the interface
implementation returned by the Iterator. Please note the 's' at the
end of the name!
The setter for a child tag object that sets one
child should look like this:
public void setchildtype(childtype child);
where childtype is the name of the interface representing the
child tag being set.
The setter for a child tag object that can set one
or more children should look like this:
public void addchildtype(childtype child);
public void addchildtypeGrouped(childtype child);
where childtype is the name of the interface representing the
child tag being added. The second version whose name end with
"Grouped" means that the child will be added at the end of
its group. That is, if there are several children of the same type
they are considered a group and are kept together. This is important
if <xsd:sequence> is used to define children in a schema.
Namespace
If a child tag belongs to a namespace then the
childtype in the getter and setter names in the examples above
can be replaced with:
namespaceprefix_childtype
Example:
public childtype getnamespaceprefix_childtype();
public void setnamespaceprefix_childtype(childtype child);
Both the namespaceprefix and childtype have the first
character capitalized!
There is an alternative to prefixing the getter
and setter names with the namespace prefix. You can declare a
constant called QUALIFIED_NAMESPACE in your interfaces, containing
the namespace url. Then you are also required to use the third
version of the createXMLObjectBinder() mehtod that also takes a
namespace prefix if you are going to create a new XML object from
scratch. Any object created with the factory instance returned by the
binder will then have that prefix without it having to be part of the
setter and getter name.
If you define the constant in your interfaces and
unmarshall then you will only get objects that are prefixed with the
prefix defined for the namespace that the constant defines. The
unmarshall will also extract the namespace prefix, so if you
unmarshall, add/change, and then marshal, any objects created by the
factory after unmarshal will have the same namespace prefix as the
unmarshalled xml file. You don't have to care what the prefix is in
that case! See xob/examples/validating/schema/readchangewritens for
an example.
Interfaces generated by xobgen makes use of this
alternative to handling namespaces. If the schema has the
elementFormDefault=”qualified” attribute then QUALIFIED_NAMESPACE
constants will be generated for all interfaces.
Tag namespace and naming
Please note that the interface names does not
contain namespace names in them!
Attributes
The getter for an attribute should look like this:
public primitivetype getAttributename();
where Attributename is the name of the attribute with the
first character capitalized and the rest identical to the attribute
name. Please note that if the real attribute name starts with a
capital then any getter for it will fail! This is a very uncommon
case, and I have no workaround for the moment.
The setter for an attribute should look like this:
public void setAttributename(primitivetype value);
where Attributename is the name of the attribute with the
first character capitalized and the rest identical to the attribute
name.
primitivetype is one of int, long, float,
double, boolean, String, XSDDate, XSDTime, XSDDateTime, Date, XSDDay,
XSDMonth, XSDMonthDay, XSDYear, and XSDYearMonth.
The XSD* types reside in the xob.xml.datatypes
package. Date is a java.util.Date, which can be used as an
alternative to XSDDate, but not to XSDTime or XSDDateTime! The
XSDDate, XSDDateTime, and XSDTime extends java.util.Date and have
constructors that takes java.util.Date objects so they can easily be
converted back and forth to java dates. The other XSD* objects are
only value and type holders. Interpretation of their contents is up
to you!
Please note that an XSDDate toString()
value conforms to the XMLSchema date format while java.util.Date does
not!
Attribute namespace
If an attribute belongs to a namespace then
attributename should be replaced with:
namespace_attributename
There is no alternative to this, and the xobgen generator does not
handle namespaces for attributes! (Until someone can explain to me
how a namespace qualified attribute is defined in a schema I cannot
fix this. The schema documentation I have does not explain this.
--Tommy)
Tag value
The getter of the content value of a tag should
look like this:
public primitivetype getinterfacenameValue();
where interfacename is the name of the interface the getter
belongs to. This naming is to avoid collisions with a getter for a
"value" attribute, which might not be that uncommon!
The setter of the content value of a tag should
look like this:
public void setinterfacenameValue(primitivetype value);
where interfacename is the name of the interface the getter
belongs to.
Examples
The xob/examples catalog contains variants of the
purchase order xml file example that covers all features of xob. They
double as test code. The non validating and DTD validating examples
require any JAXP 1.1 parser in the classpath prior to running the ant
script. If you are using JDK 1.4 you have the crimson parser already
available.
For the schema validating examples you need a JAXP
1.2 parser. The ant scripts for those examples add the bundled Xerces
jar files to the classpath. It finds them in xob/extjars.
Tested XML parsers
XOB have been tested with Xerces 2.4.0 and the
crimson parser included with jdk 1.4 from Sun.
Configuration
In xob/lib there is an TypeMap.xml file. It maps
XML Schema types to XOB types. It lists the valid XOB types to map
to. It should contain mappings for all XML Schema datatypes listed by
the XML Schema tutorial at www.w3schools.org.
XOB 2.1 and earlier versions had the type map
hardcoded. If there is any XML Schema datatype missing, you can just
add it to the map, but please also inform me.
Please note that the xob/lib/TypeMap.xsd file is
only included as a convenience if you want to validate the
TypeMap.xml file when editing it. By default the TypeMap.xsd does not
reference the TypeMap.xsd for validation since that requires JAXP 1.2
or later. XOB does not try to validate it when it reads it even if
you add the schema reference to it!
The type mapping only affects the generator
(xobgen.jar)! xob.jar which is needed runtime does not reference
xob/lib/TypeMap.xml in any way!
Author/contact
|