Introduction

This page demonstrates how to deal with the CSV header, both in reading a CSV file and writing a CSV file.

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

Note that in order to properly parse your CSV, you need to know in advance if there is a header line.

Our example does contain a header line, so we need to account for that when we parse the CSV.

Additionally, when we write our Person Objects to CSV, we want a header line.

Configuration

In order to account for the header line during parsing, there is no need to alter the configuration. The header is accounted for at runtime based on how we invoke the CsvToObjectMapper.

However, in order to use a header line when we are outputting the CSV from an Object, we must add a property to our CsvFieldMappings configuration:

                                <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>

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" />
                        </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>

Invokation

We demonstrated the invokation of the CsvToObjectMapper while accounting for the CSV header in Mapping from CSV to Object. It is reviewed here:

 import org.springframework.core.io.FileSystemResource;
 import org.springframework.core.io.Resource;

 import com.projectnine.csvmapper.CsvToObjectMapper;
 import com.projectnine.spring.SpringLoader;

 public class ExampleMappingCsvToObject {

    public static void main(String[] args) {
        // There are other ways of loading an ApplicationContext, but this is
        // very convenient.
        SpringLoader.loadSpringConfiguration("csvObjectMapper-test.xml");
        
        // Just a simple FileSystemResource.
            Resource csvResource = new FileSystemResource("sampleCsv.csv");
            
            // A new CsvToObjectMapper using the Resource we just defined,
            // true to indicate that the CSV file has a header, and the
            // "personMappingDefinition" we defined earlier.
            CsvToObjectMapper csvToObjectMapper = new CsvToObjectMapper(
                    csvResource, true, "personMappingDefinition");
                    
                // You can do this all day. Just know that when it starts returning
                // null, it's out of CSV. And of course, Exceptions can be thrown if
                // the CSV is invalid.
            Person p = (Person) csvToObjectMapper.generateNextObjectFromCsv();
    }
 } 

Note that if there is no header, the second argument of the CsvToObjectMapper constructor would be false.

There are two ways of printing a header line in our CSV output when mapping from Object to CSV:

 import java.util.List;
 import java.util.Vector;

 import com.projectnine.csvmapper.ObjectToCsvMapper;

 public class ExampleMappingObjectToCsv {

    public static void main(String[] args) throws Exception {
        Person p = new Person();
        // Set some properties

        // Either spit out CSV to a file from a List
        List<Person> list = new Vector<Person>();
        list.add(p);
        ObjectToCsvMapper objectToCsvMapper = new ObjectToCsvMapper(
                "personMappingDefinition", "/path/to/some/file.csv");
        
        // The second argument instructs the objectToCsvMapper to write the
        // header line first.
        objectToCsvMapper.writeObjectsToCsv(list, true);

        // Or spit out a single line of CSV based on an Object
        System.out.println(ObjectToCsvMapper.createHeader("personMappingDefinition"));
        System.out.println(ObjectToCsvMapper.convertObjectToCsv(p,
                "personMappingDefinition"));
    }
 }