This page demonstrates how to create a CSV to Object mapping configuration and how to use the configuration to begin generating Java Objects from CSV.
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 String 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(String zipCode) { this.zipCode = zipCode; } public String 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; } }
Note that the CSV Object Mapper does not care if the configuration is in an ApplicationContext by itself or not.
Whether you're mapping CSV to Objects, Objects to CSV, or both, your configuration will start the same:
<?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> </beans>
That is, you will always declare a CsvToObjectMapper type bean, and that bean will always contain references to the mappings that you will be using. You may use any number of mappings. We are only using one here for simplicity's sake.
The mapping that we will be using will look something like this:
... <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="columnIndex" value="0" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setLastName(%ARGUMENT%)" /> <property name="columnIndex" value="1" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" /> <property name="columnIndex" value="2" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" /> <property name="columnIndex" value="3" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" /> <property name="columnIndex" value="4" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setZipCode(%ARGUMENT%)" /> <property name="columnIndex" value="5" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setHomePhoneNumber(%ARGUMENT%)" /> <property name="columnIndex" value="6" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCellPhoneNumber(%ARGUMENT%)" /> <property name="columnIndex" value="7" /> </bean> </list> </property> <property name="beanClassName" value="Person" /> <property name="expectedNumberOfFields" value="8" /> </bean> ...
The mapping definition contains the following general information: the name of the class to which the mapping applies (beanClassName), how many fields should be expected in the CSV (expectedNumberOfFields -- optional), and the list of field mappings (fieldMappings) that describe which columns in the CSV file go to which properties of the mapped Object.
The field mappings themselves contain two important pieces of information: the zero-indexed index of a column (columnIndex) in the CSV file and a corresponding mutator method (csvToObjectExpression) on the mapped Object.
Our finished configuration looks something 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="columnIndex" value="0" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setLastName(%ARGUMENT%)" /> <property name="columnIndex" value="1" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setStreetAddress(%ARGUMENT%)" /> <property name="columnIndex" value="2" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCity(%ARGUMENT%)" /> <property name="columnIndex" value="3" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setState(%ARGUMENT%)" /> <property name="columnIndex" value="4" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setZipCode(%ARGUMENT%)" /> <property name="columnIndex" value="5" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setHomePhoneNumber(%ARGUMENT%)" /> <property name="columnIndex" value="6" /> </bean> <bean class="com.projectnine.csvmapper.CsvFieldMapping"> <property name="csvToObjectExpression" value="setCellPhoneNumber(%ARGUMENT%)" /> <property name="columnIndex" value="7" /> </bean> </list> </property> <property name="beanClassName" value="Person" /> <property name="expectedNumberOfFields" value="8" /> </bean> </beans>
In general, invokation consists of four steps: load the configuration, instantiate an instance of org.springframework.core.io.Resource containing the CSV from which you will be generating Java Objects, instantiate a com.projectnine.csvmapper.CsvToObjectMapper that corresponds to your chosen mapping, and generate Objects from the CsvToObjectMapper.
Here is how that might look in code:
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(); } }