Getting Started

Probably the best way to get started is to look at some examples. Here is some simple example code that reads and writes a bean (together with some comments). It's a good place to start.

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 in various ways. A couple of examples of these different ways follow.

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 (basic)

.betwixt files

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'>
<hide property='something'/>
<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'/>
    <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, their 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.

One common requirement when mapping beans to xml is that the property and type names from the bean must be processed (in some way) before they are used in the xml. For example, a property called WebApp might need to be mapped to an element called web-app .

Betwixt supports customization of these mappings through plug-in implementations of the org.apache.commons.betwixt.strategy.NameMapper interface. It is often useful to allow different mappings for elements and attribute and so different implementations can be set for elements and attributes. The default NameMapper implementation simply returns the type name without modification.

Note that the attribute and element names given in a .betwixt file (as usual) override the name mapping settings on the XMLIntrotrospector .

Using a custom type name to element name mapping

Betwixt supports pluggable conversion of type names to element names. Setting the ElementNameMapper property on an XMLIntrospector determines how names from the bean will be converted into element names.

Using a custom property name to attribute name mapping

Betwixt supports pluggable conversion of type names to attribute names. Setting the AttributeNameMapper property on an XMLIntrospector determines how names from the bean will be converted into attribute names.

Custom Mapping Example

Here's a simple bean which will be mapped to xml:

public class TallTreeBean {

    private float heightOfTree;
    
    public TallTreeBean(float height) {
        setHeightOfTree(height);
    }
    
    public float getHeightOfTree() {
        return heightOfTree;
    }	
    
    public void setHeightOfTree(float heightOfTree) {
        this.heightOfTree = heightOfTree;
    }
}	

Next is an application that writes that bean to xml. Custom name mappers for elements and attributes are set.

import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.betwixt.strategy.DecapitalizeNameMapper;
import org.apache.commons.betwixt.strategy.HyphenatedNameMapper;

public class NameMapperExampleApp {
    
    public static final void main(String args[]) throws Exception{
        
        // create write and set basic properties
	BeanWriter writer = new BeanWriter();
        writer.getXMLIntrospector().setAttributesForPrimitives(true);
        writer.enablePrettyPrint();
        writer.setWriteIDs(false);
        
        // set a custom name mapper for attributes
        writer.getXMLIntrospector().setAttributeNameMapper(new HyphenatedNameMapper());
        // set a custom name mapper for elements
        writer.getXMLIntrospector().setElementNameMapper(new DecapitalizeNameMapper());
        
        // write out the bean
        writer.write(new TallTreeBean(15.1f));
        System.out.println("");
    }
    
}

The application should print out (to System.out) an xml fragment which looks like:

<tallTreeBean height-of-tree="15.1"/>

As you can see, the first letter of the element name has been decapitalized and the capitals in the property separated by hyphens after being converted to lower case.

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.

Using a custom plural stemmer

Betwixt allows this auto-detection of plurals from singulars to be customized. Implementations of org.apache.commons.betwixt.PluralStemmer allow different strategies for this mapping to be plugged into XMLIntrospector . The strategy used by XMLIntrospector to match singlular properties and plural methods is determined by the PluralStemmer property value.

One important usage of custom plural stemmers is to support classes with non-english method names. A custom PluralStemmer implementation can be created containing the plural rules for the language. Betwixt will then be able to recognize matching plural and singular methods.

The default implementation supports common english plural patterns and then falls back to finding any property that starts with the singular name. For example, it will match a plural property called FooBars for a singular property called FooBar .

Reading And Writing Map Properties

Maps are a special kind of composite property. Each entry in a map has a key and a value. Betwixt handles maps by adding extra elements which wrap each entry. Each entry is wrapped in a <entry> element. That element contains the key wrapped in a <key> element and the entry value wrapped in a <value> element.

The structure is something like:

    ...
    <entry>
        <key>...</key>
        <value>...</value>
    <entry>
    <entry>
        <key>...</key>
        <value>...</value>
    <entry>
    ...
The key and the value content are standard betwixt mappings of the objects.

Reading map properties is an extension of the ways that Betwixt handles collections. Rather than searching for an add* method that takes a single parameter, now Betwixt looks (in a similar fashion) for such a method that takes two parameters.

Customizing the mapping of a bean to XML (advanced)

Caching and XMLRegistry

Introspection is slow and so caching the results improves preformance. Though the writers and readers can - by nature - only be used safely in a single thread, a single XMLIntrospector instance can be safely shared amongst multiple threads. Sharing a single instance will improve performance by sharing it's XMLBeanInfo cache.

The XMLBeanInfoRegistry interface allows finely grained, pluggable control over the caching strategy used by a XMLIntrospector . The implementation used can be set by passing an instance to XMLIntrospector.setRegistry .

Before using the standard introspection techniques to create a new XMLBeanInfo instance for a bean, the current XMLBeanInfoRegistry is first checked. Only if the registry does not return an XMLBeanInfo will a new one be created. Once a new instance has been created by introspection, the XMLBeanInfoRegistry implementation will be called so that the XMLBeanInfo can (if required) be cached.

The default strategy caches everything and supports flushes. Betwixt contains an alternative implementation that does not cache anything. Users that require more sophisticated caching strategies should create custom implementations.

The XMLBeanInfoRegistry can also be used to override the standard introspection mechanism on a per class basis. The safest way to do this is to create a custom XMLBeanInfoRegistry implementation that pre-loads XMLBeanInfo 's for the required classes. If flush is called, the cache should be reset that it contains only those that it contained at the start.

Other XMLIntrospector Settings

Here are discussed the important settings that haven't been covered already.

The AttributesForPrimitives property determines whether a primitive type (including strings) should be - by default - mapped to elements or attributes. For example, a property called Age of a bean called PersonBean would be mapped to something like:

        <PersonBean>
            <age>21</age>
            ...
when isAttributesForPrimitives is false but to
        <PersonBean age='21'>
            ...
when it is true.

The WrapCollectionsInElement property determines whether the elements for a composite property (ie one that returns a collection or iterator) should be wrapped in a parent element. For example, if isWrapCollectionsInElement is true then a property with signature List getChildren() would wrap a <children> element around the elements representing the contents of the list.

Using .betwixt files To Read And Write Mixed Content

An element with mixed content contains child elements and text. For example, element foo has mixed content in the following:

<foo>
        Some text
        <bar/>
    </foo>
Betwixt supports writing mixed content through text elements in the .betwixt file.

A text element can be mapped to a property in which case it must have a property attribute and may (optionally) have a type attribute. Otherwise, the text element is mapped to a static value, in which case it must have a value attribute. If a text element has both value and property attributes then an exception will be thrown.

For example, a simple bean with the .betwixt file

<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="attribute">
    <element name='foo'>
        <attribute name='version' value='1.0'/>
        <element name='bar'>
            <attribute name='version' property='alpha'/>
            <text value='static text'/>
            <element name='baa' property='beta'/>
            <text property='gamma'/>
        </element>
    </element>
</info>
and with property values alpha='One', beta='Two' and gamma='Three' will write an output like:
<foo version="1.0">
    <bar version="One">static text<baa>Two</baa>Three</bar>
</foo>

Betwixt supports reading back mixed content in one special situation which happily is also a common use case. Betwixt will call a single property with all the mixed content text. So, only one mixed content property is specified then the bean can be written and then read back.

Using ConvertUtils To Customize Conversion Of Primitives

ConvertUtils is part of commons-beanutils and it can be used to flexibly convert strings to objects and back again. Betwixt uses ConvertUtils to perform these conversions and so standard ConvertUtils methods can be called to customize these conversions.

Writing Beans (Advanced)

Writing DynaBeans

A DynaBean is a pseudo-bean whose properties may differ between instances of the same Class. Pseudo-introspection of the available properties uses a pseudo-class of type DynaClass. For more details see BeanUtils .

Betwixt now supports the (correct) writing of objects implementating DynaBean. Rather than using standard introspection for these objects, Betwixt now interrogates the DynaClass. Using .betwixt files with DynaBeans is not yet supported and special behaviour of DynaBeans can be overridden by specifying a .betwixt file.

Reading Beans (Advanced)

Adding Custom Digestion Rules

Betwixt builds on Apache Digester for bean reading. BeanReader extends Digester and this makes a wide range of functionality available.

Digester uses Rule 's to specify the xml mapping (for more details see the Digester documentation ). Betwixt provides a custom ruleset ( BeanRuleSet ). This creates Rule 's that implement the standard Betwixt mapping for a class. BeanReader.registerBeanClass uses this RuleSet to add these standard betwixt mapping Rule 's for the bean class.

These standard Betwixt mapping rules can be integrated with other Digester Rule 's. Rule 's added before registerBeanClass is called will come before (in a Digester sense) the standard betwixt Rule 's. Those added after will come afterwards.

Note that care must be taken with the patterns for additional Rule 's. The standard Betwixt mapping will only work if all it's Rule 's are matched.

Advanced Updaters

Betwixt by default uses the property write method for standard properties and matched stems for composite properties (for more details, see here ) to update property values when reading beans. These approaches should be sufficient for most cases. But this can be overruled on a per element basis.

By using a .betwixt file, the method used to update the bean can be controlled on a per element basis. When the value of the updater attribute of an <element> element is set, Betwixt will try to find a bean method with a matching name which takes a single parameter. When such a matching method exists, this will be called to update the property value.

For example, the following betwixt file fragment:

<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="element">
  <element name="bean">
        ...
        <element name='name' property='name' updater='nameSetter'/>
        ...
  </element>
</info>
will look for a method called 'nameSetter' and use that to update the value mapped to the 'name' element.

Examples

A Simple Example

This is a simple example to help those new to betwixt. It shows how a simple bean can be converted to xml and back again. A round trip, no less!

In order to run these simple examples, the classpath needs to contain Betwixt and all it's dependencies . Note that any JAXP (1.1 or later) compliant parser can be used to replace xerces and xml-apis. JUnit is not required to run betwixt (but is needed if you want to run the unit tests in CVS).

This example is based around a very simple bean representing a person:

public class PersonBean {
    
    private String name;
    private int age;
    
    /** Need to allow bean to be created via reflection */
    public PersonBean() {}
    
    public PersonBean(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }	
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String toString() {
        return "PersonBean[name='" + name + "',age='" + age + "']";
    }
}

The basic usage pattern for writing beans using Betwixt is to create a BeanWriter, configure it and then pass a bean into the write method. Pretty easy.

Here's a simple application which converts a person bean to xml which is then sent to System.out :

import java.io.StringWriter;

import org.apache.commons.betwixt.io.BeanWriter;

public class WriteExampleApp {

    /** 
     * Create an example bean and then convert it to xml.
     */
    public static final void main(String [] args) throws Exception {
        
        // Start by preparing the writer
        // We'll write to a string 
        StringWriter outputWriter = new StringWriter(); 
        
        // Betwixt just writes out the bean as a fragment
        // So if we want well-formed xml, we need to add the prolog
        outputWriter.write("<?xml version='1.0' ?>");
        
        // Create a BeanWriter which writes to our prepared stream
        BeanWriter beanWriter = new BeanWriter(outputWriter);
        
        // Configure betwixt
        // For more details see java docs or later in the main documentation
        beanWriter.getXMLIntrospector().setAttributesForPrimitives(false);
        beanWriter.setWriteIDs(false);
        beanWriter.enablePrettyPrint();
        
        // Write example bean as base element 'person'
        beanWriter.write("person", new PersonBean("John Smith", 21));
        
        // Write to System.out
        // (We could have used the empty constructor for BeanWriter 
        // but this way is more instructive)
        System.out.println(outputWriter.toString());
    }
}

Ths basic usage pattern for reading beans is only a little more complex. This time, you need to create a BeanReader, configure it, register the bean classes that the xml will be mapped to and then parse should be called.

Here's a simple application that converts some xml to a person bean. The bean is then converted to string and the result sent to System.out :

import java.io.StringReader;

import org.apache.commons.betwixt.io.BeanReader;

public class ReadExampleApp {
    
    public static final void main(String args[]) throws Exception{
        
        // First construct the xml which will be read in
        // For this example, read in from a hard coded string
        StringReader xmlReader = new StringReader(
                    "<?xml version='1.0' ?><person><age>25</age><name>James Smith</name></person>");
        
        // Now convert this to a bean using betwixt
        // Create BeanReader
        BeanReader beanReader  = new BeanReader();
        
        // Configure the reader
        // If you're round-tripping, make sure that the configurations are compatible!
        beanReader.getXMLIntrospector().setAttributesForPrimitives(false);
        beanReader.setMatchIDs(false);
        
        // Register beans so that betwixt knows what the xml is to be converted to
        // Since the element mapped to a PersonBean isn't called the same, 
        // need to register the path as well
        beanReader.registerBeanClass("person", PersonBean.class);
        
        // Now we parse the xml
        PersonBean person = (PersonBean) beanReader.parse(xmlReader);
        
        // send bean to system out
        System.out.println(person);
    }
    
}

A Rich Site Summary Mapping

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