Interfaces And Abstract ClassesUnderstanding The ProblemBetwixt 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 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); } } 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); } 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; } } 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> Reading Beans (Advanced)Adding Custom Digestion Rules
Betwixt builds on
Digester uses
These standard Betwixt mapping rules can be integrated with other Digester
Note that care must be taken with the patterns for additional Advanced UpdatersBetwixt 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 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> Reading Beans - In DepthUnderstanding 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 Customizing Bean Creation
The chain used by the BeanReader is part of the 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 BeanCreationList chain = BeanCreationList.createStandardChain(); BeanCreator creator = MyBeanCreator(); chain.insertBeanCreator(1, creator);
Another useful class is Example: EnumsHerein 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; } } 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); ... Using Bean Includes
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. |