OpenCsv writes wrong column names with BeanToCsv + HeaderColumnNameTranslateMappingStrategy

Baduel picture Baduel · Nov 18, 2015 · Viewed 13.7k times · Source

I'm using opencsv 3.6 in order to create a csv file starting from a java bean.

First of all, I tried this code:

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.bean.BeanToCsv;
import com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class CustomBean {

    private String name;
    private String surname;

    public CustomBean(String n, String s) {
        this.name = n;
        this.surname = s;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getSurname() {
        return surname;
    }

    public static void main(String[] args) {
        Map<String,String> mapping = new HashMap<String,String>();
        mapping.put("COLUMN1","name");
        mapping.put("COLUMN2","surname");

        HeaderColumnNameTranslateMappingStrategy<CustomBean> strategy = new HeaderColumnNameTranslateMappingStrategy<CustomBean>();
        strategy.setType(CustomBean.class);
        strategy.setColumnMapping(mapping);

        ArrayList<CustomBean> customUsers = new ArrayList<CustomBean>();
        customUsers.add(new CustomBean("Kobe","Bryant"));

        BeanToCsv<CustomBean> bean = new BeanToCsv<CustomBean>();

        try {            
            CSVWriter writer = new CSVWriter(new FileWriter("testOut.csv"));
            bean.write(strategy, writer, customUsers);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

But I had the following error:

Exception in thread "main" java.lang.RuntimeException: Error writing CSV !
    at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:74)
    at test.CustomBean.main(CustomBean.java:63)
Caused by: java.lang.NullPointerException
    at com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy.getColumnName(HeaderColumnNameTranslateMappingStrategy.java:45)
    at com.opencsv.bean.HeaderColumnNameMappingStrategy.findDescriptor(HeaderColumnNameMappingStrategy.java:112)
    at com.opencsv.bean.BeanToCsv.processHeader(BeanToCsv.java:103)
    at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:69)
    ... 1 more

This happens because in opencsv source code in getColumnName method in the HeaderColumnNameTranslateMappingStrategy class there is the following line:

return col < header.length ? columnMapping.get(header[col].toUpperCase()) : null;

Therefore, header is null. This is true, in fact this class is a subclass of HeaderColumnNameMappingStrategy class that contains the header variable (String[] type) that is never initialized.

The only useful method I found in this class is captureHeader, but unfortunately it takes a CSVReader as input.

For this reason I created an empty csv file:

COLUMN1,COLUMN2

and I added the following lines at the beginning of the try/catch block:

CSVReader reader = new CSVReader(new FileReader("testIn.csv"));
strategy.captureHeader(reader);

In this way (that I really don't like because I have to create a csv file) I have no exception, but in the result csv file the name of the columns does not follow the mapping strategy:

"name","surname"
"Kobe","Bryant"

Two questions:

  1. How can I have the expected result, i.e. the right column names in the csv file?
  2. There is a way to not use the CSVReader class?

Answer

Krishnaraj picture Krishnaraj · Nov 18, 2015

Looking at the source code of BeanToCsv, the processHeader(...) method does nothing with the supplied headers. Your only option is to create a custom strategy ( to avoid CSVReader ) and a custom BeanToCsv as below

 

public class CustomBean {
   ...
   public static void main(String[] args) {
     ...
     HeaderColumnNameTranslateMappingStrategy strategy = new CustomStrategy<CustomBean>();
     strategy.setType(CustomBean.class);
     strategy.setColumnMapping(mapping);
     ...

     BeanToCsv bean = new CustomBeanToCsv<CustomBean>();
     ...
   }

   static class CustomStrategy<T> extends HeaderColumnNameTranslateMappingStrategy {

    @Override
    public void setColumnMapping(Map columnMapping) {
        super.setColumnMapping(columnMapping);
        header = new String[columnMapping.size()];
        int i = 0;
        for (Map.Entry entry : columnMapping.entrySet()) {
            header[i] = entry.getKey().toUpperCase();
            i++;
        }
    }

     public String[] getHeader() {
         return header;
     }
   }

   static class CustomBeanToCsv<T> extends BeanToCsv {
     @Override
     protected String[] processHeader(MappingStrategy mapper) throws IntrospectionException {
         if (mapper instanceof CustomStrategy) {
             return ((CustomStrategy) mapper).getHeader();
         } else {
             return super.processHeader(mapper);
         }
     }
   }
}