This is a simple step-by-step
Betwixt tutorial focussing
on creating a mapping for an RSS 0.91 application.
Betwixt is a dynamic, start-from-java XML-object binder.
For those unfamiliar with these terms, this means that it maps XML <-> objects
defining a binding from an existing group of java objects into xml (and back again)
without the use of code generation.
So, the place we'll start is with an object model for RSS 0.91. This was originally created for Digester by Craig R. McClanahan and Ted Husted with a few amendments for this tutorial. There are only four main objects in the model:
The application simply processes an xml file in RSS format and creates a plain text version. This isn't really very useful but is a good demonstration of bean reading.
RSS 0.91 is an XML vocabulary for describing metadata about websites commonly used to supply news channels. RSS 0.91 is a little elderly and has quite a simple - but elegant - DTD:
<!ELEMENT rss (channel)> <!ATTLIST rss version CDATA #REQUIRED> <!-- must be "0.91"> --> <!ELEMENT channel ( title | description | link | language | item+ | rating? | image? | textinput? | copyright? | pubDate? | lastBuildDate? | docs? | managingEditor? | webMaster? | skipHours? | skipDays?)*> <!ELEMENT title (#PCDATA)> <!ELEMENT description (#PCDATA)> <!ELEMENT link (#PCDATA)> <!ELEMENT image (title | url | link | width? | height? | description?)*> <!ELEMENT url (#PCDATA)> <!ELEMENT item (title | link | description)*> <!ELEMENT textinput (title | description | name | link)*> <!ELEMENT name (#PCDATA)> <!ELEMENT rating (#PCDATA)> <!ELEMENT language (#PCDATA)> <!ELEMENT width (#PCDATA)> <!ELEMENT height (#PCDATA)> <!ELEMENT copyright (#PCDATA)> <!ELEMENT pubDate (#PCDATA)> <!ELEMENT lastBuildDate (#PCDATA)> <!ELEMENT docs (#PCDATA)> <!ELEMENT managingEditor (#PCDATA)> <!ELEMENT webMaster (#PCDATA)> <!ELEMENT hour (#PCDATA)> <!ELEMENT day (#PCDATA)> <!ELEMENT skipHours (hour+)> <!ELEMENT skipDays (day+)>
Betwixt is start-from-java, a DTD is not required.
This has many advantages during prototyping but this tutorial will
concentrate on a target specified by DTD (since it's a little more instructive).
What's probably needed in both cases is a good understanding of the xml
that the object model maps to.
Every xml document has a root node.
Betwixt mapping starts from a root object.
The process of creating a mapping between the xml document and the object model
Betwixt typically begins by working out the structural relationship
between the elements in the xml and the objects in the property subgraph of that root object.
is the root object mapping to the
<channel> root element
of the xml. There are several ways that a mapping for an object can be defined in
Betwixt. In this case, a
.betwixt xml file will be used.
This following file is built into the jar in the package containing
<?xml version="1.0" encoding="UTF-8" ?> <info primitiveTypes="element"> <element name="rss"> <attribute name="version" property="version"/> <element name="channel"> <element name="title" property="title"/> <element name="item" property="items"/> <element name="textinput" property="textInput"/> <element name="skipDays"> <element name="day" property="skipDays"/> </element> <element name="skipHours"> <element name="hour" property="skipHours"/> </element> <addDefaults/> </element> </element> </info>
This describes how elements and attributes in the xml are mapped to properties
in a Channel object.
Betwixt uses the type of the properties to
determine how to map the sub-graph under an element mapped to a property.
So, for example the
textInput property gets and sets a
object so the mapping for
TextInput is used.
There are a number of collective properties: those that are mapped to a many
objects (rather than just one). For example, the
property is backed by an
List and is mapped to zero, one or more
Betwixt looks for an adder method (something like
addItem) which matches
the collective property (which should return something like a Collection)
and uses that to add instances of the object created from the xml contained.
This mechanism can be customized but the defaults are fine in this case.
The standard mapping for the class contained with the collection is used.
<addDefaults/> tag. This tells
to use it's introspection system to automagically determine the mappings
for all other properties.
Betwixt has a number of powerful
strategy plugins which allow the introspection process to be controlled
in a finely-grained manner but in this case, the default mappings of the
remaining properties work fine :)
See binding for more details.
Image uses a smaller
<?xml version="1.0" encoding="UTF-8" ?> <info primitiveTypes="element"> <element name="image"> <element name="url" property="URL"/> <addDefaults/> </element> </info>
is an example of the other (main) way that mappings are created for objects.
There is no
.betwixt file so
Betwixt introspects the
class using reflection and determines a mapping based on the current introspector
configuration. In this case, it's exactly what's required so there's no need to change
TextInput is also mapped by using introspection.
org.apache.commons.betwixt.io.BeanReader reads beans from xml documents.
reads an xml file (specified on the command line)
and prints a plain text version. The root object (in this case
needs to be
can be used to parse an XML document and produce a bean.