Interfaces And Abstract Classes

Understanding The Problem

Betwixt uses the type of the corresponding mapped property to determine the class to be instantiated when reading xml. But what happens when the type of the mapped property is an interface or an abstract class and so cannot be instantiated? Well - unless steps are taken to solve this problem, the read will fail.

Betwixt provides a number of different ways to solve this problem. One solution is to use derived beans . This is flexible but means coupling the xml to a java class structure. Another solution is to use custom bean creation to ensure that an appropriate class is created. Other solutions follow in this section.

Specifying An Implementation Class In The Betwixt File

The class to be instantiated when a (mapped) element is read can be specified via the class attribute. When present, this should be a fully qualified java class name. A bean of this type will then be instantiated when the element is read.

For example, here's a bean:

package org.apache.commons.betwixt.example;

import java.util.ArrayList;
import java.util.List;

public class ExampleBean {
    
    private String name;
    private List examples = new ArrayList();
    
    public ExampleBean() {}

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public List getExamples() {
        return examples;
    }
    
    public void addExample(IExample example) {
        examples.add(example);
    }
}
which contains a List whose contents each implement the IExample interface:
package org.apache.commons.betwixt.example;

public interface IExample {

    public int getId();
    public void setId(int id);
    public String getName();
    public void setName(String id);
}
Betwixt will create an instance of the following standard implementation:
package org.apache.commons.betwixt.example;

public class ExampleImpl implements IExample {
    
    private int id;
    private String name;
    
    public ExampleImpl() {}
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}
for each example element if the following ExampleBean.betwixt file:
<?xml version="1.0" encoding="UTF-8" ?>
<info>
    <element name='example-bean'>
        <element name='example' property='examples' class='org.apache.commons.betwixt.example.ExampleImpl'/>
        <addDefaults/>
    </element>
</info>
is used.

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.

Reading Beans - In Depth

Understanding The Class Creation Chain

Betwixt uses the Chain Of Responsibility pattern to decide the object which should be created for a particular element. The standard chain contains ChainedBeanCreators which implement functionality such as standard bean creation based on the expected type and the returning of beans by ID. This allows users to easily insert their own ChainedBeanCreators into the chain - or to replace the standard Betwixt chain completely.

Customizing Bean Creation

The chain used by the BeanReader is part of the ReadConfiguration and is accessed via the BeanCreationChain property. For example the following sets a custom chain.

        BeanCreationChain chain = MyBeanCreationChain();
        BeanReader reader = new BeanReader();
        ...
        reader.registerBeanClass("bean", Bean.class);
        reader.getReadConfiguration().setBeanCreationChain(chain);
        ...
        Bean bean = (Bean) reader.parse(in);
        ...

Betwixt provides a standard (list-backed) chain called BeanCreationList. This provides an easy methods to register your own ChainedBeanCreator. It also provides a factory method which creates an instance with the standard betwixt chain already set. For example, the following inserts a custom in second place:

        BeanCreationList chain = BeanCreationList.createStandardChain();
        BeanCreator creator = MyBeanCreator();
        chain.insertBeanCreator(1, creator);

Another useful class is ChainedBeanCreationFactory. This contains factory methods for the BeanCreators used by Betwixt. This allows a user to easily mix custom and standard creators.

Example: Enums

Herein is contained a practical example demonstrating how custom bean creation may be used.

A common java pattern is the use of strongly typed Enum classes. Let's say that you have the following class:

public class CompassPoint {

    public static final CompassPoint NORTH = new CompassPoint("North");
    public static final CompassPoint SOUTH = new CompassPoint("South");
    public static final CompassPoint EAST = new CompassPoint("East");
    public static final CompassPoint WEST = new CompassPoint("West");
    
    private String name;
    
    private CompassPoint(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
and you have a bean which you would like to map to xml and back using Betwixt. Betwixt is bean-centric. It aims to make mapping beans easy. But, CompassPoint objects are not beans and do not have the empty constructors that Betwixt requires.

A good way to solve this problem is to create a custom BeanCreator which knows how to create an enum of the right type from the 'name' attribute value. For example:

public class CompassPointEnumCreator implements ChainedBeanCreator {
    public Object create(ElementMapping mapping, ReadContext context, BeanCreationChain chain) {
        if (CompassPoint.class.equals(mapping.getType())) {
            String value = mapping.getAttributes().getValue("name");
            if ("North".equals(value)) {
                return CompassPoint.NORTH;
            }
            if ("South".equals(value)) {
                return CompassPoint.SOUTH;
            }
            if ("East".equals(value)) {
                return CompassPoint.EAST;
            }
            if ("West".equals(value)) {
                return CompassPoint.WEST;
            }
        }
        return chain.create(mapping, context);
    }
}

Once this class has been created, all that remains is to add this into the chain. In this case, it's probably most convenient to use the factory method to create a standard chain and then insert the BeanCreator at a suitable position:

    BeanCreationList chain = BeanCreationList.createStandardChain();
    chain.insertBeanCreator(1, new EnumCreator());
    ...
    BeanReader reader = new BeanReader();
    reader.getXMLIntrospector().setAttributesForPrimitives(true);
    reader.getXMLIntrospector().setWrapCollectionsInElement(false);
    reader.getReadConfiguration().setBeanCreationChain(chain);
    ...
Now you're ready to start reading enums!

Using Bean Includes

IncludeBeanCreator is a ChainedBeanCreator that implements a system of 'includes' files. Adding this class to the bean creation chain will load and read the file specified by the include-file attribute whenever it is encountered. The file will be loaded as a resource by the current ClassLoader using the standard rules.

A similar effect could be accumplished through the using of xml entity's. There are occasions when this is inconvenient and so it's useful to have an alternative available.

Reading Polymorphic Mappings

A polymorphic mapping is one where the decision about the element type and name is postponed from introspection time to bind time. This allows reading of collections containing mixed types distinguished by element name. For example:

<?xml version="1.0" ?>
<container>
  <elementA/>
  <elementB/>
</container>

Polymorphic mappings should be set up using dot betwixt files. The name attribute for the element should be omitted and mappings registered for the contained object types. It is usually more convenient to use a single file containing all the mappings. For example:

<?xml version="1.0"?>
<betwixt-config>
  <class name='SomeContainer'>
    <element name='container'>
      <!-- Polymorphic so no name attribute -->
      <element property='element'/>
    </element>
  </class>
  <!-- Need to register mappings for types of contained elements 
       (when Betwixt default strategy is used) -->
  <class name='ContainedElementA'>
    <element name='elementA'/>
  </class>
  <class name='ContainedElementB'>
    <element name='elementB'/>
  </class>
</betwixt-config>

By default, in this circumstance Betwixt will try to guess the correct resolution by searching all registered XMLBeanInfo's for an appropriate match. If more than one is found, an arbitrary one is used. In many cases, this accords well with intuition. There are occasions when more finely grained control may be required. The resolution is therefore factored into PolymorphicReferenceResolver (a pluggable strategy) on XMLIntrospector. A custom resolver allows alternative algorithms for determining type to be used which can (for example) ignore the mappings registered. Note that the default implementation is provided by the default XMLBeanInfoRegistry implementation. Therefore, when using a custom registry a custom resolver must also be used.

Note: when using mixed collections with dot betwixt files containing addDefaults it may be necessary to set the guess-names attribute to false.