Overview
JBeanMapper is a java library for converting javabeans to javabeans using rules defined in an XML mapping document.
To use JBeanMapper, calling code "registers" mappings with the BeanMapper
. These mappings can be registered in code explicitly, or from an XML mapping document. Calling code then simply calls map on the BeanMapper to perform the conversion.
Installation
Download and put in your classpath JBeanMapper jar, commons-beanutils-1.6.1.jar, commons-digester-1.5.jar, and commons-logging-1.0.2.jar.
XML Mappings Documents
The schema for xml mappings is found here, and an explanation of the elements follows.
Simple Property Mappings
Most mappings can most easily be expressed using an XML document. The XML document contains the rules for the JavaBean transformation. Suppose we want to map ComplexPersonBean to ComplexPersonBean2 where the beans are defined as:
public class ComplexPersonBean { private String name; private String title; private String age; private ComplexAddressBean address; public ComplexAddressBean getAddress() { return address; } public void setAddress(ComplexAddressBean address) { this.address = address; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } public class ComplexAddressBean { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } public class ComplexPersonBean2 { private String myName; private int myAge; private ComplexAddressBean2 myAddress; public String getMyName() { return myName; } public void setMyName(String myName) { this.myName = myName; } public ComplexAddressBean2 getMyAddress() { return myAddress; } public void setMyAddress(ComplexAddressBean2 myAddress) { this.myAddress = myAddress; } public int getMyAge() { return myAge; } public void setMyAge(int myAge) { this.myAge = myAge; } } public class ComplexAddressBean2 { private String myAddress; private String title; public String getMyAddress() { return myAddress; } public void setMyAddress(String myAddress) { this.myAddress = myAddress; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }For the first example, we'll just map the properties
name
, and address
of ComplexPersonBean
to properties myName
and myAddress
of ComplexPersonBean2
. The XML document would look like:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.ComplexPersonBean" target="org.jbeanmapper.docs.ComplexPersonBean2"> <property-mapping src="name" target="myName"/> <property-mapping src="address" target="myAddress"/> </bean-mapping> <bean-mapping src="org.jbeanmapper.docs.ComplexAddressBean" target="org.jbeanmapper.docs.ComplexAddressBean2" > <property-mapping src="address" target="myAddress"/> </bean-mapping> </bean-mappings>
The following code could be used to perform the transform:
ComplexPersonBean personBean = new ComplexPersonBean(); personBean.setName("Henry"); ComplexAddressBean addressBean = new ComplexAddressBean(); addressBean.setAddress("12 Here Street"); personBean.setAddress(addressBean); BeanMapper mapper = new BeanMapper(); InputSource source = new InputSource(new FileReader("/path/to/mappingfile/mapping.xml")); mapper.registerBeanMappings(source); ComplexPersonBean2 personBean2 = (ComplexPersonBean2)mapper.map(personBean);
Default Property Mappings
The<default-property-mappings>
tag is used to populate the bean mapping with the default property mappings. The default property mappings are all property mappings from the source bean to the target bean that meet the following criteria:
- The properties have not already been explicitly mapped with a <property-mapping> tag.
- The property names are equal (see String.equals(Object))
- The property is not included in the "excludes" attribute of the default-property-mappings element
public class PersonBean1 { private String name; private String address; private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } } public class SimplePerson { private String name; private String address; private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }The following xml can be used to map all properties of
PersonBean1
to all properties of SimplePerson
:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.PersonBean1" target="org.jbeanmapper.docs.SimplePerson"> <default-property-mappings/> </bean-mapping> </bean-mappings>To map only the phone property of
PersonBean1
to SimplePerson
either of the two following xml documents could be used:
Using the default-property-mappings tag:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.PersonBean1" target="org.jbeanmapper.docs.SimplePerson"> <default-property-mappings excludes="address, name"/> </bean-mapping> </bean-mappings>Using the property-mapping tag:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.PersonBean1" target="org.jbeanmapper.docs.SimplePerson"> <property-mapping src="phone" target="phone"/> </bean-mapping> </bean-mappings>
Custom Creators
Suppose that rather than relying on JBeanMapper's default implementation to instantiateComplexPesonBean2
, we want to use a Factory method or perform
some other special instantiation. This can be done by implementing the BeanCreator
interface.
The BeanCreator
interface has a single method:
public Object createBean(Class clazz, MappingContext context);
Suppose a ComplexPersonBean2Creator
was created that implements BeanCreator
.
To use the new ComplexPersonBean2Creator
would require a simple change to the mapping file
to indicate that a custom creator is should be used:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.ComplexPersonBean" target="org.jbeanmapper.docs.ComplexPersonBean2" creator="org.jbeanmapper.docs.ComplexPersonBean2Creator"> <property-mapping src="name" target="myName"/> <property-mapping src="address" target="myAddress"/> </bean-mapping> <bean-mapping src="org.jbeanmapper.docs.ComplexAddressBean" target="org.jbeanmapper.docs.ComplexAddressBean2" > <property-mapping src="address" target="myAddress"/> </bean-mapping> </bean-mappings>
Converters
Now suppose we wanted to map theage
property of ComplexPersonBean
to the myAge
property of
ComplexPersonBean2
. In ComplexPersonBean
the property age
is a String
. In
ComplexPersonBean2
the myAge
property is an int
. To convert from the String to the int, we can
create a BeanConverter
or use any of the classes that implement
org.apache.commons.beanutils.Converter
defined in the apache commons-beanutils
project. For this example, to use the IntegerConverter
from commons-beanutils, the mapping file would look like:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.ComplexPersonBean" target="org.jbeanmapper.docs.ComplexPersonBean2" creator="org.jbeanmapper.docs.ComplexPersonBean2Creator"> <property-mapping src="name" target="myName"/> <property-mapping src="age" target="myAge" converter="org.apache.commons.beanutils.converters.IntegerConverter"/> <property-mapping src="address" target="myAddress"/> </bean-mapping> <bean-mapping src="org.jbeanmapper.docs.ComplexAddressBean" target="org.jbeanmapper.docs.ComplexAddressBean2" > <property-mapping src="address" target="myAddress"/> </bean-mapping> </bean-mappings>
Nested target-bean properties
Often, a bean needs to map a property to a nested property of the target bean. For example, suppose we want to map the
title
property of ComplexPersonBean
to the title
property of the myAddress
property
of ComplexPersonBean2
. This can be done using a "dot" notation to indicate nested beans. The mapping file would look like:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.ComplexPersonBean" target="org.jbeanmapper.docs.ComplexPersonBean2" creator="org.jbeanmapper.docs.ComplexPersonBean2Creator"> <property-mapping src="name" target="myName"/> <property-mapping src="age" target="myAge" converter="org.apache.commons.beanutils.converters.IntegerConverter"/> <property-mapping src="address" target="myAddress"/> <property-mapping src="title" target="myAddress.title"/> </bean-mapping> <bean-mapping src="org.jbeanmapper.docs.ComplexAddressBean" target="org.jbeanmapper.docs.ComplexAddressBean2" > <property-mapping src="address" target="myAddress"/> </bean-mapping> </bean-mappings>
Custom PropertyMapper
On rare occasions, there may be a need to completely handle the mapping of a property. By implement thePropertyMapper
interface, the complete handling of a mapping can be customized. A custom PropertyMapper
can be specified in the
XML document as follows:
<bean-mappings> <bean-mapping src="org.jbeanmapper.docs.ComplexPersonBean" target="org.jbeanmapper.docs.ComplexPersonBean2" creator="org.jbeanmapper.docs.ComplexPersonBean2Creator"> <property-mapping src="name" target="myName" mapping-class"org.jbeanmapper.docs.MyPropertyMapper"/> <property-mapping src="age" target="myAge" converter="org.apache.commons.beanutils.converters.IntegerConverter"/> <property-mapping src="address" target="myAddress"/> <property-mapping src="title" target="myAddress.title"/> </bean-mapping> <bean-mapping src="org.jbeanmapper.docs.ComplexAddressBean" target="org.jbeanmapper.docs.ComplexAddressBean2" > <property-mapping src="address" target="myAddress"/> </bean-mapping> </bean-mappings>
Collections, Arrays, and Maps
Collections, Arrays and Maps are supported in JBeanMapper. If a Collection or Map is encountered as a root object (the value passed into BeanMapper.map()), the return object will of the same type as the Collection or Map parameter. If an array is encountered as a parameter, the return object will be an array of the type as indicated by the mapping for the component type of the parameter array.If a Collection is encountered as a property of a javabean, it will be mapped to the Collection or array type of the destination property. If the destination property is not a Collection or Array, mapping will fail with a PropertyMappingException.
If an array is encountered as a property of a javabean, it will be mapped to the Collection or array type of the destination property. If the destination property is not a Collection or Array, mapping will fail with a PropertyMappingException.
If a Map is encountered as a property of a javabean, it will be mapped to the Map type of the destination property. If the destination property is not a Map, mapping will fail with a PropertyMappingException.
Creating Simple Mappings Programmatically
If the source and target JavaBeans have the same property names and types, they can be registered very simply with the BeanMapper's register method. Below is a simple example registering a simple mapping with the BeanMapper. Suppose we have the two JavaBeans below:
public class PersonBean1 { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class SimplePerson { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
The following code can be used to map from PersonBean1 to a SimplePerson:
BeanMapper mapper = new BeanMapper(); PersonBean1 personBean1 = new PersonBean1(); personBean1.setName("test"); SimplePerson personBean2 = (SimplePerson)mapper.map(personBean1, SimplePerson.class);
Creating Complex Mappings Programmatically
Complex mappings can be registered programmatically by creating a BeanMapping object and adding it to the BeanMapper with the addBeanMapping method.