Tutorial

This is a simple step-by-step Betwixt tutorial focussing on creating a mapping for an RSS 0.91 application.

The Object Model

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.

The XML

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+)>

Since 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.

Mapping Channel and Image

Every xml document has a root node. Every Betwixt mapping starts from a root object. The process of creating a mapping between the xml document and the object model in 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.

Channel 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 Channel:


<?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 TextInput 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 items property is backed by an List and is mapped to zero, one or more <item> tags. 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.

Notice the <addDefaults/> tag. This tells Betwixt 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 .betwixt file:


<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="element">
  <element name="image">
    <element name="url" property="URL"/>
    <addDefaults/>
  </element>
</info>

Mapping Item and TextInput

Item 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.

Reading A Bean From XML

org.apache.commons.betwixt.io.BeanReader reads beans from xml documents. The tutorial application reads an xml file (specified on the command line) and prints a plain text version. The root object (in this case Channel) needs to be registered first. Then parse can be used to parse an XML document and produce a bean.