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.

Customized Mapping (Advanced)

Caching and the 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.

More finely grained control over which primitive properties are mapped to elements and which to attributes can be supplied by creating a custom SimpleTypeMapper strategy and plugging that into the introspection configuration.

Here is an example implementation (which maps strings to elements and everything else to attributes):

    
    /** Implementation binds strings to elements but everything else to attributes */
    public class StringsAsElementsSimpleTypeMapper extends SimpleTypeMapper {
        /**
         * Binds strings to elements but everything else to attributes
         */
        public Binding bind(
                            String propertyName, 
                            Class propertyType, 
                            IntrospectionConfiguration configuration) {
            if (String.class.equals(propertyType)) {
                return SimpleTypeMapper.Binding.ELEMENT;
            }
            return SimpleTypeMapper.Binding.ATTRIBUTE;
        }
    }

This class can be used to configure Betwixt so that strings are bounds to elements but other primitives are bound to attributes as follows:

     
        XMLIntrospector introspector = new XMLIntrospector();
        introspector.getConfiguration().setSimpleTypeMapper(
            new StringsAsElementsSimpleTypeMapper());
        ...

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.

Introspection And Normalization

When an Object is introspected by the XMLIntrospector, the class is examined and an XMLBeanInfo created based upon it. But there are occasions when some variation is needed - for example when reading or writing Entity Beans or Proxy implementations.

Betwixt provides the ClassNormalizer strategy class as a way to allow users to customise the process by which Betwixt works out which Class should be introspected for a given Object. The default implementation simply supplies the Class of the Object. But by setting the classNormalizer property of XMLIntrospector, a custom implementation can be used instead.

Betwixt supplies a second implementation called ListedClassNormalizer. This contains a list of classes to match together with the Class which should be returned when the Class is matched.

For example, take a class FaceImpl that implements an interface IFace. Betwixt will introspect every instance of FaceImpl as if it just implemented IFace by use of the following code:

        XMLIntrospector introspector = ...;
        ListedClassNormalizer classNormalizer = new ListedClassNormalizer();
        classNormalizer.addSubstitution( IFace.class );
        introspector.setClassNormalizer( classNormalizer );

Converting Objects And Primitives To Strings (And Back Again)

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. By default, Betwixt uses ConvertUtils to perform these conversions and so standard ConvertUtils methods can be called to customize these conversions.

Converting Dates (And Other Objects)

There are numerous situations when read beans from xml or writing beans to xml that String to Object or Object to String conversions are required. Betwixt uses a Strategy class to allow a convenient default which will work well for most basic users whilst allowing advanced users to fully hook in and customize this process.

The default strategy uses ConvertUtils from commons-beanutils to perform these conversions. This is a powerful component that allows flexible customization of the conversion process.

There is one exception to this rule. If the class is a java.util.Date - or a subclass of java.util.Date which is not a subclass of java.sql.Date, java.sql.Time or java.sql.Timestamp - then this is converted to and from a string following this pattern:

    EEE MMM dd HH:mm:ss zzz yyyy
(using the SimpleDateFormat notation). Observent readers will realise that this is the same pattern that is returned by java.util.Date.toString - and that's why this pattern was chosen. It provides a good default for casual users.

Advanced users will probably need a particular date format. The recommended way to do this is through registering appropriate converters with ConvertUtils. The default conversion strategy must also be replaced with an instance of ConvertUtilsObjectStringConverter . This is set though a BindingConfiguration property.

For example, to delegate to ConvertUtils for all conversions in a read:

    BeanReader reader = new BeanReader();
    reader.getBindingConfiguration().setObjectStringConverter(new ConvertUtilsObjectStringConverter());
    reader.parse...
and in a write:
    BeanWriter writer = new BeanWriter();
    writer.getBindingConfiguration().setObjectStringConverter(new ConvertUtilsObjectStringConverter());
    writer.write...

Custom ObjectStringConverters (Advanced)

ConvertUtils is flexible and powerful. It comes with a range of Converter implementations which allow quick and easy customization. But, there are occasions where this will not suit all the requirements of the user. Betwixt supports this need by allowing a custom ObjectStringConverter to be plugged in.

The strategy class ObjectStringConverter is simple: containing only two simple methods. For more information about creating subclasses, see the javadocs. The implementation to be used is set through the BindingConfiguration ObjectStringConverter property.

For example, to set a custom ObjectStringConverter for all conversions in a read:

    ObjectStringConverter converter = new MyObjectStringConverter();
    BeanReader reader = new BeanReader();
    reader.getBindingConfiguration(converter);
    reader.parse...
and in a write:
    ObjectStringConverter converter = new MyObjectStringConverter();
    BeanWriter writer = new BeanWriter();
    writer.getBindingConfiguration(converter);
    writer.write...

Betwixt is distributed with a range of ObjectStringConverter's in the org.apache.commons.betwixt.strategy package. Examining the source code for these classes is a good please to start when creating your own implementation.

(Brief) Guide To Creating Custom Strategy Plugins

It is common for users of Betwixt to need to develop their own custom strategies to handle some parts of the binding process. This section contains some information that may of of some use for those people. Help to create a more comprehensive guide would be appreciated.

Using Options

Options provide an extensible way for extra mapping information to be communicated from the binding to those components executing the mapping. Each Descriptor exposes an Options property . This contains a set of values indexed by name (both are strings). These options can be set programmatically during the binding. They can also be set through the .betwixt file.

Setting option values through the .betwixt file is easy: just add an <option> child element to an <element> element. For example, the XMLBeanInfo for the following betwixt file:


<?xml version='1.0'?>
<info primitiveTypes="attribute">
	<element name='some-bean'>
		<element name='some-property' property='someProperty'>
			<option>
				<name>org.apache.commons.betwixt.example-one</name>
				<value>value-one</value>
			</option>
		</element>
		<element name='another-property' property='anotherProperty'>
			<option>
				<name>org.apache.commons.betwixt.example-two</name>
				<value>value-two</value>
			</option>
		</element>
	</element>
</info>

will have the value value-one associated with the option name org.apache.commons.betwixt.example-one in the options for the some-property descriptor and value-two associated with org.apache.commons.betwixt.example-two for another-property. Note that the last value set for a particular option name is the one that will be used.

The recommended convention for naming option's is to prefix with the reverse domain name (the same convension that is used for the standard naming of packages). In any case, all option names beginning with org.apache should be avoided since these may be used by ASF products in the future.

At the moment, there is no support for inheritance of options (from parent to child) through this may be considered later if there is sufficient demand.