Getting Started

Probably the best way to get started is to look at some examples. The best example to start with is the Ant target "demo.rss" which runs the RSSBeanWriter sample program in the src/test directory. Once you've got the Apache Commons build system working, by installing Ant and creating your own build.properties to point to the required JARs type the following at a command line

ant demo.rss

This uses the Commons Digester RSSDigester example to parse an RSS document, create a Channel bean and then write it out again as XML using the default XMLIntrospector and the BeanWriter. You should see the XML come out from the Channel bean which looks similar to a real RSS document.

The next example to look at is

ant demo.rss2

This is similar to the above but uses a BeanReader to parse the RSS file. So this is Betwixt defaulting the Digester rules required to parse the XML document. Then the BeanWriter is used to output the beans that get created.

Mapping beans to XML

There are various ways of mapping beans to an XML structure. For example consider a simple bean

public class CustomerBean {
    public String getName();
    public Order[] getOrders();
    public String[] getEmailAddresses();
}

This could be mapped to XML as these various ways

Example 1

This example uses attributes for primitive types.

<CustomerBean name="James">
    <order id="1">...</order>
    <order id="2">...</order>
    <emailAddress>jstrachan@apache.org</emailAddress>
</CustomerBean>

Example 2

This example uses elements for all properties and wraps collections in an extra element (which can be quite common in XML schemas). Also note that some element names have been changed.

<customer>
    <name>James</name>
    <orders>
        <order id="1">...</order>
        <order id="2">...</order>
    </orders>
    <email-addresses>
        <email-address>jstrachan@apache.org</email-address>
    </email-addresses>
</customer>    

Betwixt aims to provide a diversity of possible mappings such that the developer can choose, if they wish, how their beans appear as XML to support whatever XML encoding scheme that is desired. If no particular mapping is provided then Betwixt will create a default mapping for you. Also the customization mechanism allows you to just override the parts you want to and let Betwixt default the rest. So if you just want to rename a property in XML for a certain type, thats all you need to do. No need to hand-code what Betwixt can deduce for itself.

Customizing the mapping of a bean to XML

The XMLIntrospector will look for files of the form className.betwixt on the classpath using the same ClassLoader used to load the given class and use that document to specify the mapping to XML. If this file does not exist then the default introspection rules are used.

The simplest possible file may just set the name of the element. e.g.

<?xml version="1.0" encoding="UTF-8" ?>
<info>
  <element name="channel"/>
  <addDefaults/>
</info>

The above means to use the name 'channel' for the outer most element for the given type. The <addDefaults> means to add the defaults from the introspector. This allows you to just rename a few properties then let the introspector do the rest. There is also a <hide> element which allows one or more properties to be hidden. Also note that the <element> and <attribute> tags can be nested to any kind of depth allowing whatever XML structure you wish. This can be useful if you wish to wrap collections in some arbitrary collection tags or to group properties of a bean together in some XML structure. e.g.

<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="attribute">
  <element name="channel"/>
    <element name="customerList">
      <element name="customer" property="customers"/>
    </element>
    <element name="foo">
      <attribute name="qqq" property="ppp"/>
      <element name="bar" property="xyz"/>
    <hide property="something"/>
    <addDefaults/>
  </element>
</info>

Note that the .betwixt file specifies the mapping for a single bean. So, whilst you can add extra elements (as above), it can't be used to specify to names for child beans through nesting element elements.

The primitiveTypes attribute in the <info> element is optional and can be used to specify whether primitive java types (strings, numbers, dates etc) are specified as attributes or elements by default.

Finally static text can be specified using a value attribute inside an <element> or <attribute> tag. e.g. to add constant attributes such as a version label to the generated XML...

<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="element">
  <element name="rss"/>
    <attribute name="version" value="0.91"/>
    <element name="channel"/>
    <addDefaults/>
  </element>
</info>

Bean naming conventions

The Java Beans specification contains various naming conventions that should be used when writing beans that will allow the beans introspector to automatically guess the properties in a bean and their getters & setter methods etc. Betwixt will use these same naming conventions to deduce how to make the beans appear as XML. There are some other naming conventions that you can use to make your beans easier to output as XML or parse.

Using adder methods for composite properties

This naming convention is used to indicate the singular type of some composite property.

To use: create an add method to match the getter method for 'composite properties'.

public class SomeBean {
    public <CollectionType> getFoo*();
    public void addFoo(<SingularType> foo);
}

Where CollectionType can be an array, a Collection, Enumeration, Iterator, Map. The [SinglularType] refers to the type of an item in the collection. The name of the getter property starts with 'Foo'. So 'Foo' is the singular name, the plural collection name could be Foos, FooArray, FooList, FooIterator or some other encoding, though the plural name should start with the singular name for auto-detection to work properly.

Examples

In the RSS example from Digester there's a bean which matches this pattern.

public class Channel

    public Item[] getItems();

    public void addItem(Item item);
}

This means that the following bean does not match this naming convention, since the plural property name does not start with the singular name..

public class Foo {
    public Collection getPeople();
    public void addPerson(Person person);
}

Though these two beans do match

public class Foo {
    public Collection getPersonCollection();
    public void addPerson(Person person);
}
public class Foo {
    public Iterator getPersonIterator();
    public void addPerson(Person person);
}

The following are other valid examples of composite-getter methods and their matching adder methods.

Composite getter method Adder method
getChildren() addChild()
getPersonList() addPerson()
getItems() addItem()
getChannels() addChannel()
getSheep() addSheep()