Introduction

This page demonstrates how to use the CsvFieldFormatter classes to format a a Java Object and convert it to a String for insertion into CSV.

Examples

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 Java Object that looks like this:

 public class Person {
        private String firstName;
        private String lastName;
        private String streetAddress;
        private String city;
        private String state;
        private Integer zipCode;
        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 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;
        }
        
        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;
        }
 }

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" />
                                </bean>
                                <bean class="com.projectnine.csvmapper.CsvFieldMapping">
                                        <property name="csvToObjectExpression" value="setLastName(%ARGUMENT%)" />
                                        <property name="objectToCsvExpression" value="person.getLastName()" />
                                        <property name="columnIndex" value="1" />
                                </bean>
                                <bean class="com.projectnine.csvmapper.CsvFieldMapping">
                                        <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" />
                                        <property name="objectToCsvExpression" value="person.getStreetAddress()" />
                                        <property name="columnIndex" value="2" />
                                </bean>
                                <bean class="com.projectnine.csvmapper.CsvFieldMapping">
                                        <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" />
                                        <property name="objectToCsvExpression" value="person.getCity()" />
                                        <property name="columnIndex" value="3" />
                                </bean>
                                <bean class="com.projectnine.csvmapper.CsvFieldMapping">
                                        <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" />
                                        <property name="objectToCsvExpression" value="person.getState()" />
                                        <property name="columnIndex" value="4" />
                                </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" />
                                </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" />
                                </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" />
                                </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>

Requirements

We want to be able to convert a Person Object into CSV that looks exactly like the source CSV.

This is where the formatObject on the CsvFieldFormatter comes in.

Configuration

If you have already configured your mapping for formatting from Strings to Objects, then your configuration is complete. See the formatting primer for further details.

Implementation

If you recall from the formatting primer, we left the formatObject method as a no-op. Now, let's fill in the functionality.

Here is how the IntegerFormatter might look in code:

 package com.projectnine.csvmapper.examples;

 import com.projectnine.csvmapper.CsvFieldFormatter;

 public class IntegerFormatter implements CsvFieldFormatter {

    public String formatObject(Object object) {
        return object.toString();
    }

    public Object formatString(String rawPropertyValue) {
        return new Integer(rawPropertyValue);
    }
 }

Here is how the PhoneNumberFormatter might look in code:

 package com.projectnine.csvmapper.examples;

 import com.projectnine.csvmapper.CsvFieldFormatter;

 public class PhoneNumberFormatter implements CsvFieldFormatter {
    private static String separator;

    public void setSeparator(String separator) {
        PhoneNumberFormatter.separator = separator;
    }

    public String formatObject(Object object) {
        String[] phoneNumberParts = object.toString().split(separator);
        StringBuffer buffer = new StringBuffer();
        for (String part : phoneNumberParts) {
            buffer.append(part);
        }
        return buffer.toString();
    }

    public Object formatString(String rawPropertyValue) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(rawPropertyValue.substring(0, 3)).append(separator);
        buffer.append(rawPropertyValue.substring(3, 6)).append(separator);
        buffer.append(rawPropertyValue.substring(6));

        return buffer.toString();
    }
 }