You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2003/08/14 23:25:26 UTC
cvs commit: jakarta-commons/betwixt/xdocs/guide binding.xml
rdonkin 2003/08/14 14:25:26
Added: betwixt/xdocs/guide binding.xml
Log:
Split Overview into separate documents in guide directory.
Revision Changes Path
1.1 jakarta-commons/betwixt/xdocs/guide/binding.xml
Index: binding.xml
===================================================================
<?xml version="1.0"?>
<document>
<properties>
<title>Binding Bean To XML</title>
<author email="jstrachan@apache.org">James Strachan</author>
</properties>
<body>
<section name="Bean Naming Conventions">
<p>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.
</p>
<p>
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 <code>WebApp</code> might need to be mapped to an element called <code>web-app</code>.
</p>
<p>
Betwixt supports customization of these mappings through plug-in implementations of the
<code>org.apache.commons.betwixt.strategy.NameMapper</code> 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.
</p>
<p>
<strong>Note</strong> that the attribute and element names given in a .betwixt file (as usual)
override the name mapping settings on the <code>XMLIntrotrospector</code>.
</p>
<subsection name="Using A Custom Type Name To Element Name Mapping">
<p>
Betwixt supports pluggable conversion of type names to element names. Setting the
<code>ElementNameMapper</code> property on an <code>XMLIntrospector</code> determines
how names from the bean will be converted into element names.
</p>
</subsection>
<subsection name="Using A Custom Property Name To Attribute Name Mapping">
<p>
Betwixt supports pluggable conversion of type names to attribute names. Setting the
<code>AttributeNameMapper</code> property on an <code>XMLIntrospector</code> determines
how names from the bean will be converted into attribute names.
</p>
</subsection>
<subsection name="Custom Mapping Example">
<p>
Here's a simple bean which will be mapped to xml:
<source><![CDATA[
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;
}
}
]]></source>
</p>
<p>
Next is an application that writes that bean to xml. Custom name mappers for elements
and attributes are set.
<source><![CDATA[
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("");
}
}
]]></source>
</p>
<p>
The application should print out (to System.out) an xml fragment which looks like:
<source><![CDATA[
<tallTreeBean height-of-tree="15.1"/>
]]></source>
</p>
<p>
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.
</p>
</subsection>
</section>
<section name="Using Adder Methods For Composite Properties">
<p>
This naming convention is used to indicate the singular type of some composite property.
</p>
<p>
To use: create an add method to match the getter method for 'composite
properties'.
</p>
<source><![CDATA[public class SomeBean {
public <CollectionType> getFoo*();
public void addFoo(<SingularType> foo);
}]]></source>
<p>
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.
</p>
<subsection name="Using A Custom Plural Stemmer">
<p>
Betwixt allows this auto-detection of plurals from singulars to be customized.
Implementations of <code>org.apache.commons.betwixt.PluralStemmer</code> allow different
strategies for this mapping to be plugged into <code>XMLIntrospector</code>.
The strategy used by <code>XMLIntrospector</code> to match singlular properties and plural methods
is determined by the <code>PluralStemmer</code> property value.
</p>
<p>
One important usage of custom plural stemmers is to support classes with non-english method names.
A custom <code>PluralStemmer</code> implementation can be created containing the plural rules for
the language. Betwixt will then be able to recognize matching plural and singular methods.
</p>
<p>
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
<code>FooBars</code> for a singular property called <code>FooBar</code>.
</p>
</subsection>
<subsection name="Reading And Writing Map Properties">
<p>
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 <code><entry></code> element. That element contains the key wrapped in a <code><key></code>
element and the entry value wrapped in a <code><value></code> element.
</p>
<p>
The structure is something like:
<source><![CDATA[
...
<entry>
<key>...</key>
<value>...</value>
<entry>
<entry>
<key>...</key>
<value>...</value>
<entry>
...
]]></source>
The key and the value content are standard betwixt mappings of the objects.
</p>
<p>
Reading map properties is an extension of the ways that Betwixt handles collections. Rather than
searching for an <code>add*</code> method that takes a single parameter, now Betwixt looks
(in a similar fashion) for such a method that takes two parameters.
</p>
</subsection>
</section>
<section name="Customized Mapping (Advanced)">
<subsection name="Caching and the XMLRegistry">
<p>
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
<code>XMLIntrospector</code> instance can be safely shared amongst multiple threads. Sharing a
single instance will improve performance by sharing it's <code>XMLBeanInfo</code> cache.
</p>
<p>
The <code>XMLBeanInfoRegistry</code> interface allows finely grained, pluggable control over
the caching strategy used by a <code>XMLIntrospector</code>. The implementation used can be set
by passing an instance to <code>XMLIntrospector.setRegistry</code>.
</p>
<p>
Before using the standard introspection techniques to create a new <code>XMLBeanInfo</code> instance
for a bean, the current <code>XMLBeanInfoRegistry</code> is first checked. Only if the registry
does not return an <code>XMLBeanInfo</code> will a new one be created. Once a new instance has been
created by introspection, the <code>XMLBeanInfoRegistry</code> implementation will be called so that
the <code>XMLBeanInfo</code> can (if required) be cached.
</p>
<p>
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.
</p>
<p>
The <code>XMLBeanInfoRegistry</code> 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 <code>XMLBeanInfoRegistry</code>
implementation that pre-loads <code>XMLBeanInfo</code>'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.
</p>
</subsection>
<subsection name="Other XMLIntrospector Settings">
<p>
Here are discussed the important settings that haven't been covered already.
</p>
<p>
The <strong><code>AttributesForPrimitives</code></strong> property determines whether a primitive
type (including strings) should be - by default - mapped to elements or attributes. For example, a
property called <code>Age</code> of a bean called <code>PersonBean</code> would be mapped to something
like:
<source><![CDATA[
<PersonBean>
<age>21</age>
...
]]></source>
when <code>isAttributesForPrimitives</code> is false but to
<source><![CDATA[
<PersonBean age='21'>
...
]]></source>
when it is true.
</p>
<p>
The <strong><code>WrapCollectionsInElement</code></strong> 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 <code>isWrapCollectionsInElement</code> is true then a property with signature
<code>List getChildren()</code> would wrap a <code><children></code> element around the elements
representing the contents of the list.
</p>
</subsection>
<subsection name="Using .betwixt files To Read And Write Mixed Content">
<p>
An element with mixed content contains child elements and text.
For example, element <code>foo</code> has mixed content in the following:
<source>
<![CDATA[<foo>
Some text
<bar/>
</foo>]]>
</source>
Betwixt supports writing mixed content through <code>text</code> elements in the
.betwixt file.
</p>
<p>
A <code>text</code> element can be mapped to a property in which case it must have
a <code>property</code> attribute and may (optionally) have a <code>type</code> attribute.
Otherwise, the <code>text</code> element is mapped to a static value, in which case it
must have a <code>value</code> attribute. If a <code>text</code> element has both
<code>value</code> and <code>property</code> attributes then an exception will be thrown.
</p>
<p>
For example, a simple bean with the .betwixt file
<source><![CDATA[<?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>]]>
</source>
and with property values alpha='One', beta='Two' and gamma='Three' will write an output like:
<source>
<![CDATA[<foo version="1.0">
<bar version="One">static text<baa>Two</baa>Three</bar>
</foo>
]]></source>
</p>
<p>
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.
</p>
</subsection>
<subsection name='Using ConvertUtils To Customize Conversion Of Primitives'>
<p>
<code>ConvertUtils</code> is part of <a href='http://jakarta.apache.org/commons/beanutils.html'>commons-beanutils</a>
and it can be used to flexibly convert strings to objects and back again. Betwixt uses ConvertUtils to
perform these conversions and so standard <code>ConvertUtils</code> methods can be called to customize these
conversions.
</p>
</subsection>
</section>
</body>
</document>