This page demonstrates how to map a CSV Mapping to a CSV Field Mapping of another mapping.
Note that any CSV file should be valid as long as it complies with RFC 4180. If you wish to process non-compliant CSV files, you will have to wait until the next release of the CSV Object Mapper.
For the purposes of this demo, let's say that we have a CSV file with the following content:
"First Name", "Last Name", "Street Address", "City", "State", "Zip Code", "Home Phone Number", "Cell Phone Number" "John", "Doe", "123 Test Dr.", "Test City", "HI", "11111", 1231231234, 5554443333 "Jane", "Doe", "321 Tset Dr.", "Tset City", "IA", "99999", 4324324321, 3334445555
Note that the Java Objects to which you map your CSV may be POJOs employing the standard [set/get]ter properties. However, your Objects may be much more "fancy" if you like.
For the purposes of this demo, we want to map to a pair of Java Objects that look like this:
public class Person { private String firstName; private String lastName; private Address address; private String homePhoneNumber; private String cellPhoneNumber; public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getLastName() { return lastName; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public void setHomePhoneNumber(String homePhoneNumber) { this.homePhoneNumber = homePhoneNumber; } public String getHomePhoneNumber() { return homePhoneNumber; } public void setCellPhoneNumber(String cellPhoneNumber) { this.cellPhoneNumber = cellPhoneNumber; } public String getCellPhoneNumber() { return cellPhoneNumber; } } public class Address { private String streetAddress; private String city; private String state; private Integer zipCode; public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } public String getStreetAddress() { return streetAddress; } public void setCity(String city) { this.city = city; } public String getCity() { return city; } public void setState(String state) { this.state = state; } public String getState() { return state; } public void setZipCode(Integer zipCode) { this.zipCode = zipCode; } public Integer getZipCode() { return zipCode; } }
We also have a pre-existing mapping file which we want to expand with mapping information for converting Java Objects into CSV. It looks like this:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean id="csvToObjectMapper" class="com.projectnine.csvmapper.CsvToObjectMapper"> <property name="csvMappingDefinitions"> <map> <entry key="personMappingDefinition" value-ref="personMappingDefinition" /> </map> </property> </bean> <bean id="personMappingDefinition" class="com.projectnine.csvmapper.CsvMappingDefinition"> <property name="fieldMappings"> <list> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setFirstName(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getFirstName()" /> <property name="columnIndex" value="0" /> <property name="csvFieldHeader" value="First Name" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setLastName(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getLastName()" /> <property name="columnIndex" value="1" /> <property name="csvFieldHeader" value="Last Name" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getStreetAddress()" /> <property name="columnIndex" value="2" /> <property name="csvFieldHeader" value="Street Address" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getCity()" /> <property name="columnIndex" value="3" /> <property name="csvFieldHeader" value="City" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getState()" /> <property name="columnIndex" value="4" /> <property name="csvFieldHeader" value="State" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setZipCode(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getZipCode()" /> <property name="columnIndex" value="5" /> <property name="formatter" ref="integerFormatter" /> <property name="validationCommand" ref="numericalValidator" /> <property name="csvFieldHeader" value="Zip Code" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setHomePhoneNumber(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getHomePhoneNumber()" /> <property name="columnIndex" value="6" /> <property name="formatter" ref="phoneNumberFormatter" /> <property name="validationCommand" ref="phoneNumberValidation" /> <property name="csvFieldHeader" value="Home Phone Number" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCellPhoneNumber(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getCellPhoneNumber()" /> <property name="columnIndex" value="7" /> <property name="formatter" ref="phoneNumberFormatter" /> <property name="validationCommand" ref="phoneNumberValidation" /> <property name="csvFieldHeader" value="Cell Phone Number" /> </bean> </list> </property> <property name="beanClassName" value="Person" /> <property name="expectedNumberOfFields" value="8" /> <property name="beanVariableName" value="person" /> </bean> <bean id="integerFormatter" class="com.projectnine.csvmapper.example.IntegerFormatter"> </bean> <bean id="phoneNumberFormatter" class="com.projectnine.csvmapper.examples.PhoneNumberFormatter"> <property name="separator" value="." /> </bean> <bean id="tenCharacterValidator" class="com.projectnine.csvmapper.RegularExpressionCsvFieldValidator"> <property name="regularExpressions"> <list> <value>.{10}</value> </list> </property> <property name="required" value="true" /> </bean> <bean id="numericalValidator" class="com.projectnine.csvmapper.RegularExpressionCsvFieldValidator"> <property name="regularExpressions"> <list> <value>\d+</value> </list> </property> </bean> <bean id="phoneNumberValidation" class="org.apache.commons.chain.impl.ChainBase"> <constructor-arg> <list> <ref bean="tenCharacterValidator" /> <ref bean="numericalValidator" /> </list> </constructor-arg> </bean> </beans>
We have artificially complicated our Person Object. Now, one of its members is an Address type Object. Therefore we will need to adjust the mapping to account for this.
In order to map a mapping to a field of another mapping, both mappings must be declared:
... <bean id="csvToObjectMapper" class="com.projectnine.csvmapper.CsvToObjectMapper"> <property name="csvMappingDefinitions"> <map> <entry key="personMappingDefinition" value-ref="personMappingDefinition" /> <entry key="addressMappingDefinition" value-ref="addressMappingDefinition" /> </map> </property> </bean> ... <bean id="addressMappingDefinition" class="com.projectnine.csvmapper.CsvMappingDefinition"> <property name="fieldMappings"> <list> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getStreetAddress()" /> <property name="columnIndex" value="2" /> <property name="csvFieldHeader" value="Street Address" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getCity()" /> <property name="columnIndex" value="3" /> <property name="csvFieldHeader" value="City" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getState()" /> <property name="columnIndex" value="4" /> <property name="csvFieldHeader" value="State" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setZipCode(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getZipCode()" /> <property name="columnIndex" value="5" /> <property name="formatter" ref="integerFormatter" /> <property name="validationCommand" ref="numericalValidator" /> <property name="csvFieldHeader" value="Zip Code" /> </bean> </list> </property> <property name="beanClassName" value="Address" /> <property name="expectedNumberOfFields" value="8" /> <property name="beanVariableName" value="address" /> </bean> ...
Then, we create the following field mapping in the personMappingDefinition:
... <bean id="personMappingDefinition" class="com.projectnine.csvmapper.CsvMappingDefinition"> <property name="fieldMappings"> <list> ... <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setAddress(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getAddress()" /> <property name="beanName" value="addressMappingDefinition" /> </bean> ... </bean> ...
Our final configuration looks like this:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean id="csvToObjectMapper" class="com.projectnine.csvmapper.CsvToObjectMapper"> <property name="csvMappingDefinitions"> <map> <entry key="personMappingDefinition" value-ref="personMappingDefinition" /> <entry key="addressMappingDefinition" value-ref="addressMappingDefinition" /> </map> </property> </bean> <bean id="personMappingDefinition" class="com.projectnine.csvmapper.CsvMappingDefinition"> <property name="fieldMappings"> <list> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setFirstName(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getFirstName()" /> <property name="columnIndex" value="0" /> <property name="csvFieldHeader" value="First Name" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setLastName(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getLastName()" /> <property name="columnIndex" value="1" /> <property name="csvFieldHeader" value="Last Name" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setAddress(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getAddress()" /> <property name="beanName" value="addressMappingDefinition" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setHomePhoneNumber(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getHomePhoneNumber()" /> <property name="columnIndex" value="6" /> <property name="formatter" ref="phoneNumberFormatter" /> <property name="validationCommand" ref="phoneNumberValidation" /> <property name="csvFieldHeader" value="Home Phone Number" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCellPhoneNumber(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="person.getCellPhoneNumber()" /> <property name="columnIndex" value="7" /> <property name="formatter" ref="phoneNumberFormatter" /> <property name="validationCommand" ref="phoneNumberValidation" /> <property name="csvFieldHeader" value="Cell Phone Number" /> </bean> </list> </property> <property name="beanClassName" value="Person" /> <property name="expectedNumberOfFields" value="8" /> <property name="beanVariableName" value="person" /> </bean> <bean id="addressMappingDefinition" class="com.projectnine.csvmapper.CsvMappingDefinition"> <property name="fieldMappings"> <list> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getStreetAddress()" /> <property name="columnIndex" value="2" /> <property name="csvFieldHeader" value="Street Address" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getCity()" /> <property name="columnIndex" value="3" /> <property name="csvFieldHeader" value="City" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getState()" /> <property name="columnIndex" value="4" /> <property name="csvFieldHeader" value="State" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setZipCode(%ARGUMENT%)" /> <property name="objectToCsvExpression" value="address.getZipCode()" /> <property name="columnIndex" value="5" /> <property name="formatter" ref="integerFormatter" /> <property name="validationCommand" ref="numericalValidator" /> <property name="csvFieldHeader" value="Zip Code" /> </bean> </list> </property> <property name="beanClassName" value="Address" /> <property name="expectedNumberOfFields" value="8" /> <property name="beanVariableName" value="address" /> </bean> <bean id="integerFormatter" class="com.projectnine.csvmapper.example.IntegerFormatter"> </bean> <bean id="phoneNumberFormatter" class="com.projectnine.csvmapper.examples.PhoneNumberFormatter"> <property name="separator" value="." /> </bean> <bean id="tenCharacterValidator" class="com.projectnine.csvmapper.RegularExpressionCsvFieldValidator"> <property name="regularExpressions"> <list> <value>.{10}</value> </list> </property> <property name="required" value="true" /> </bean> <bean id="numericalValidator" class="com.projectnine.csvmapper.RegularExpressionCsvFieldValidator"> <property name="regularExpressions"> <list> <value>\d+</value> </list> </property> </bean> <bean id="phoneNumberValidation" class="org.apache.commons.chain.impl.ChainBase"> <constructor-arg> <list> <ref bean="tenCharacterValidator" /> <ref bean="numericalValidator" /> </list> </constructor-arg> </bean> </beans>