Search code examples
javacsvdeserializationobjectmapper

Converting from Map<String, String> to POJO


I'm creating a CSV loader class which reads records from CSV files and returns a List<T> where T is the target POJO class.

Sample CSV: test.csv

From the extracted records from CSV, it is possible to get 0 value for a Date field of a POJO class. From the sample CSV, record #2 where the value of createdDate is 0. How do I change 0 to a valid date (e.g 1970-01-01 09:00:00) first before the actual deserialization happens?

I have successfully created the process of reading a CSV file to conversion to returning List<T>.

  1. Parsing: org.apache.commons.csv
  2. Conversion: com.fasterxml.jackson.databind.ObjectMapper I am thinking of overriding some functions of my ObjectMapper to manipulate the values, but I have no idea how to do it.
private List<T> convertToObjectList(List<Map<String, String>> csvRecordMapList, Class<T> targetClass) {
  List<T> csvRecordObjList = new ArrayList<>();
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  ObjectMapper objectMapper = new ObjectMapper();
  objectMapper.setDateFormat(dateFormat);        
  objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  for(Map<String, String> recordMap : csvRecordMapList)
    csvRecordObjList.add(objectMapper.convertValue(recordMap, targetClass));

  return csvRecordObjList;
}

If I have 0 for a Date field for the target POJO class, I get the error message below (which is already expected):

Can not construct instance of java.util.Date from String value '0': not a valid representation (error: Failed to parse Date value '0': Unparseable date: "0")

Solution

  • Instead of using ObjectMapper, I used org.apache.commons.beanutils.BeanUtils and ConvertUtils. I found out that you can add a customized converter. Below are my updated source codes:

    private List<T> convertToObjectList(List<Map<String, String>> csvRecordMapList, Class<T> targetClass)
        throws IllegalAccessException, InvocationTargetException, InstantiationException {
        List<T> csvRecordObjList = new ArrayList<>();
        ConvertUtils.register(getDateConverter(), Date.class);
        for (Map<String, String> recordMap : csvRecordMapList) {
            T targetClassObj = targetClass.newInstance();
            BeanUtils.populate(targetClassObj, recordMap);
            csvRecordObjList.add(targetClassObj);
        }
    
        return csvRecordObjList;
    }
    

    And this is my customized converter:

    private static Converter getDateConverter() {
        return new Converter() {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            @Override
            public Object convert(Class classType, Object value) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = null;
                try {
                    date = value.equals("0") ? new Date(0) : sdf.parse((String) value);
                } catch (ParseException e) {
                    // Do nothing
                }
                return date;
            }
        };
    }