Reading and writing CSV files with Dozer

This page contains some examples of reading and writing CSV files using Super CSV and Dozer. For a closer look, refer to the reading and writing example source.

If you haven't already, check out the background on the Super CSV Dozer extension.

Example CSV file

Here is an example CSV file that represents responses to a survey (we'll be using this in the following reading examples). It has a header and 3 rows of data, all with 8 columns.

age,consentGiven,questionNo1,answer1,questionNo2,answer2,questionNo3,answer3
18,Y,1,Twelve,2,Albert Einstein,3,Big Bang Theory
,Y,1,Thirteen,2,Nikola Tesla,3,Stargate
42,N,1,,2,Carl Sagan,3,Star Wars

Reading with CsvDozerBeanReader

CsvDozerBeanReader is the most powerful CSV reader. The example reads each row from the example CSV file into a SurveyResponse bean , which has a Collection of Answers.

To do this requires the following field mapping (notice that the first two mappings are the same as you'd have for CsvBeanReader, but the rest use indexed and deep mapping).

private static final String[] FIELD_MAPPING = new String[] { 
        "age",                   // simple field mapping (like CsvBeanReader)
        "consentGiven",          // as above
        "answers[0].questionNo", // indexed (first element) + deep mapping
        "answers[0].answer", 
        "answers[1].questionNo", // indexed (second element) + deep mapping
        "answers[1].answer", 
        "answers[2].questionNo", 
        "answers[2].answer" };

If you are familiar with the standard CsvBeanReader, you'll notice that using CsvDozerBeanReader is very similar. The main difference is that CsvDozerBeanReader requires you to configure it (with the configureBeanMapping() method) prior to reading. You can still use the result of getHeader() as your field mapping, but you'll have to supply your own if you want to use deep mapping or index-based mapping.

/**
 * An example of reading using CsvDozerBeanReader.
 */
private static void readWithCsvDozerBeanReader() throws Exception {
        
        final CellProcessor[] processors = new CellProcessor[] { 
                new Optional(new ParseInt()), // age
                new ParseBool(),              // consent
                new ParseInt(),               // questionNo 1
                new Optional(),               // answer 1
                new ParseInt(),               // questionNo 2
                new Optional(),               // answer 2
                new ParseInt(),               // questionNo 3
                new Optional()                // answer 3
        };
        
        ICsvDozerBeanReader beanReader = null;
        try {
                beanReader = new CsvDozerBeanReader(new FileReader(CSV_FILENAME), CsvPreference.STANDARD_PREFERENCE);
                
                beanReader.getHeader(true); // ignore the header
                beanReader.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING);
                
                SurveyResponse surveyResponse;
                while( (surveyResponse = beanReader.read(SurveyResponse.class, processors)) != null ) {
                        System.out.println(String.format("lineNo=%s, rowNo=%s, surveyResponse=%s", beanReader.getLineNumber(),
                                beanReader.getRowNumber(), surveyResponse));
                }
                
        }
        finally {
                if( beanReader != null ) {
                        beanReader.close();
                }
        }
}

Output:

lineNo=2, rowNo=2, surveyResponse=SurveyResponse [age=18, consentGiven=true, answers=[Answer [questionNo=1, answer=Twelve], Answer [questionNo=2, answer=Albert Einstein], Answer [questionNo=3, answer=Big Bang Theory]]]
lineNo=3, rowNo=3, surveyResponse=SurveyResponse [age=null, consentGiven=true, answers=[Answer [questionNo=1, answer=Thirteen], Answer [questionNo=2, answer=Nikola Tesla], Answer [questionNo=3, answer=Stargate]]]
lineNo=4, rowNo=4, surveyResponse=SurveyResponse [age=42, consentGiven=false, answers=[Answer [questionNo=1, answer=null], Answer [questionNo=2, answer=Carl Sagan], Answer [questionNo=3, answer=Star Wars]]]

Indexed mapping and hints

In the above example Dozer creates each Answer because of the deep mapping (answers[0].questionNo). If you're using indexed mapping without deep mapping (e.g. answers[0]) and your array/Collection contains standard Java types (String, Integer, etc) then that's fine, but if it contains a custom type then Dozer needs a few hints for it to work correctly.

In the following example, a custom cell processor has been written to parse each answer column as an Answer bean, and this has been combined with indexed mapping. Notice that when the bean mapping is configured, a hint is specified for those indexed mappings - without it, Dozer populates each element with an empty collection (yikes!).

/**
 * An example of reading using CsvDozerBeanReader that uses indexed mapping and a cell processor
 * to read into a List of Answer beans (this requires a hint).
 */
private static void readWithCsvDozerBeanReaderUsingIndexMappingAndHints() throws Exception {
        
        // simple cell processor that creates an Answer with a value
        final CellProcessor parseAnswer = new CellProcessorAdaptor() {
                public Object execute(Object value, CsvContext context) {
                        return new Answer(null, (String) value);
                }
        };
        
        final CellProcessor[] processors = new CellProcessor[] { 
                new Optional(new ParseInt()), // age
                null,                         // consent
                null,                         // questionNo 1
                new Optional(parseAnswer),    // answer 1
                null,                         // questionNo 2
                new Optional(parseAnswer),    // answer 2
                null,                         // questionNo 3
                new Optional(parseAnswer)     // answer 3
        };
        
        // no deep mapping (answers[0].answer) required as we're using a cell processor to create the bean
        final String[] fieldMapping = {"age", null, null, "answers[0]", null, "answers[1]", null, "answers[2]"};
        
        // the indexed mappings need a hint for Dozer to work
        final Class<?>[] hintTypes = {null, null, null, Answer.class, null, Answer.class, null, Answer.class};
        
        ICsvDozerBeanReader beanReader = null;
        try {
                beanReader = new CsvDozerBeanReader(new FileReader(CSV_FILENAME), CsvPreference.STANDARD_PREFERENCE);
                
                beanReader.getHeader(true); // ignore the header
                beanReader.configureBeanMapping(SurveyResponse.class, fieldMapping, hintTypes);
                
                SurveyResponse surveyResponse;
                while( (surveyResponse = beanReader.read(SurveyResponse.class, processors)) != null ) {
                        System.out.println(String.format("lineNo=%s, rowNo=%s, surveyResponse=%s", beanReader.getLineNumber(),
                                beanReader.getRowNumber(), surveyResponse));
                }
                
        }
        finally {
                if( beanReader != null ) {
                        beanReader.close();
                }
        }
}

Output:

lineNo=2, rowNo=2, surveyResponse=SurveyResponse [age=18, consentGiven=null, answers=[Answer [questionNo=null, answer=Twelve], Answer [questionNo=null, answer=Albert Einstein], Answer [questionNo=null, answer=Big Bang Theory]]] 
lineNo=3, rowNo=3, surveyResponse=SurveyResponse [age=0, consentGiven=null, answers=[Answer [questionNo=null, answer=Thirteen], Answer [questionNo=null, answer=Nikola Tesla], Answer [questionNo=null, answer=Stargate]]]
lineNo=4, rowNo=4, surveyResponse=SurveyResponse [age=42, consentGiven=null, answers=[null, Answer [questionNo=null, answer=Carl Sagan], Answer [questionNo=null, answer=Star Wars]]]

Partial reading with CsvDozerBeanReader

Partial reading with CsvDozerBeanReader is virtually identical to CsvBeanReader. See the partial reading example in the reading example source.

Writing with CsvDozerBeanWriter

CsvDozerBeanWriter is the most powerful CSV writer. The example writes each CSV row from a SurveyResponse bean , which has a Collection of Answers.

It uses exactly the same field mapping as the reading example above, and once again you'll notice that CsvDozerBeanWriter requires you to configure it (with the configureBeanMapping() method) prior to writing.

/**
 * An example of writing using CsvDozerBeanWriter.
 */
private static void writeWithDozerCsvBeanWriter() throws Exception {
        
        final CellProcessor[] processors = new CellProcessor[] { 
                new Token(0, null),     // age
                new FmtBool("Y", "N"),  // consent
                new NotNull(),          // questionNo 1
                new Optional(),         // answer 1
                new NotNull(),          // questionNo 2
                new Optional(),         // answer 2
                new NotNull(),          // questionNo 3
                new Optional() };       // answer 4
        
        // create the survey responses to write
        SurveyResponse response1 = new SurveyResponse(18, true, Arrays.asList(new Answer(1, "Twelve"), new Answer(2,
                "Albert Einstein"), new Answer(3, "Big Bang Theory")));
        SurveyResponse response2 = new SurveyResponse(0, true, Arrays.asList(new Answer(1, "Thirteen"), new Answer(2,
                "Nikola Tesla"), new Answer(3, "Stargate")));
        SurveyResponse response3 = new SurveyResponse(42, false, Arrays.asList(new Answer(1, null), new Answer(2,
                "Carl Sagan"), new Answer(3, "Star Wars")));
        final List<SurveyResponse> surveyResponses = Arrays.asList(response1, response2, response3);
        
        ICsvDozerBeanWriter beanWriter = null;
        try {
                beanWriter = new CsvDozerBeanWriter(new FileWriter("target/writeWithCsvDozerBeanWriter.csv"),
                        CsvPreference.STANDARD_PREFERENCE);
                
                // configure the mapping from the fields to the CSV columns
                beanWriter.configureBeanMapping(SurveyResponse.class, FIELD_MAPPING);
                
                // write the header
                beanWriter.writeHeader("age", "consentGiven", "questionNo1", "answer1", "questionNo2", "answer2",
                        "questionNo3", "answer3");
                
                // write the beans
                for( final SurveyResponse surveyResponse : surveyResponses ) {
                        beanWriter.write(surveyResponse, processors);
                }
                
        }
        finally {
                if( beanWriter != null ) {
                        beanWriter.close();
                }
        }
}

Output:

lineNo=2, rowNo=2, surveyResponse=SurveyResponse [age=18, consentGiven=true, answers=[Answer [questionNo=1, answer=Twelve], Answer [questionNo=2, answer=Albert Einstein], Answer [questionNo=3, answer=Big Bang Theory]]]
lineNo=3, rowNo=3, surveyResponse=SurveyResponse [age=null, consentGiven=true, answers=[Answer [questionNo=1, answer=Thirteen], Answer [questionNo=2, answer=Nikola Tesla], Answer [questionNo=3, answer=Stargate]]]
lineNo=4, rowNo=4, surveyResponse=SurveyResponse [age=42, consentGiven=false, answers=[Answer [questionNo=1, answer=null], Answer [questionNo=2, answer=Carl Sagan], Answer [questionNo=3, answer=Star Wars]]]

Partial writing with CsvDozerBeanWriter

Partial writing with CsvDozerBeanWriter is virtually identical to CsvBeanWriter. See the partial writing example in the writing example source.