Search code examples
javacsvjava-8dozersupercsv

NoSuchMethodException: java.time.LocalDateTime.<init>() reading CSV using Super CSV


I have written an entity that contains just a LocalDateTime to a CSV file using Super CSV's ICsvDozerBeanWriter and I am encountering an error when reading it back using a ICsvDozerBeanReader. I was able to successfully read and write a Date object but LocalDateTime isn't working.

I've added the super-csv-java8 dependency and the writing part appears to be working fine.

I've created a small demo application in this Github repo to replicate the problem. Run the main() method and the error will be output to the console.

This is the exception I'm getting:

2016-12-09 22:24:02.427 ERROR 50405 --- [           main] org.dozer.MappingProcessor               : Field mapping error -->
  MapId: null
  Type: null
  Source parent class: org.supercsv.io.dozer.CsvDozerBeanData
  Source field name: columns
  Source field type: class java.time.LocalDateTime
  Source field value: 2016-12-09T22:24:02.226
  Dest parent class: com.example.Entity
  Dest field name: dateTime
  Dest field type: java.time.LocalDateTime

org.dozer.MappingException: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
    at org.dozer.util.MappingUtils.throwMappingException(MappingUtils.java:82) ~[dozer-5.4.0.jar:na]
    at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:261) ~[dozer-5.4.0.jar:na]
    at org.dozer.factory.ConstructionStrategies$ByConstructor.create(ConstructionStrategies.java:245) ~[dozer-5.4.0.jar:na]
    at org.dozer.factory.DestBeanCreator.create(DestBeanCreator.java:65) ~[dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:489) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:446) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:342) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:288) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.map(MappingProcessor.java:248) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.map(MappingProcessor.java:197) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.map(MappingProcessor.java:187) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.map(MappingProcessor.java:124) [dozer-5.4.0.jar:na]
    at org.dozer.MappingProcessor.map(MappingProcessor.java:119) [dozer-5.4.0.jar:na]
    at org.dozer.DozerBeanMapper.map(DozerBeanMapper.java:120) [dozer-5.4.0.jar:na]
    at org.supercsv.io.dozer.CsvDozerBeanReader.readIntoBean(CsvDozerBeanReader.java:220) [super-csv-dozer-2.4.0.jar:na]
    at org.supercsv.io.dozer.CsvDozerBeanReader.read(CsvDozerBeanReader.java:160) [super-csv-dozer-2.4.0.jar:na]
    at com.example.DemoApplication.readEntities(DemoApplication.java:51) [classes/:na]
    at com.example.DemoApplication.main(DemoApplication.java:39) [classes/:na]
Caused by: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_66]
    at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_66]
    at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:257) ~[dozer-5.4.0.jar:na]
    ... 16 common frames omitted

org.dozer.MappingException: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
    at org.dozer.util.MappingUtils.throwMappingException(MappingUtils.java:82)
    at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:261)
    at org.dozer.factory.ConstructionStrategies$ByConstructor.create(ConstructionStrategies.java:245)
    at org.dozer.factory.DestBeanCreator.create(DestBeanCreator.java:65)
    at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:489)
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:446)
    at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:342)
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:288)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:248)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:197)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:187)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:124)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:119)
    at org.dozer.DozerBeanMapper.map(DozerBeanMapper.java:120)
    at org.supercsv.io.dozer.CsvDozerBeanReader.readIntoBean(CsvDozerBeanReader.java:220)
    at org.supercsv.io.dozer.CsvDozerBeanReader.read(CsvDozerBeanReader.java:160)
    at com.example.DemoApplication.readEntities(DemoApplication.java:51)
    at com.example.DemoApplication.main(DemoApplication.java:39)
Caused by: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:257)

Ideally I'd like to write the date to the CSV file in yyyy-MM-dd format but one step at a time!


Solution

  • While Super CSV does support reading and writing java.time.LocalDateTime via it's ParseLocalDateTime and FmtLocalDateTime cell processors (both available in the super-csv-java8 module), Dozer is trying to instantiate the destination LocalDateTime object instead of using the result of the cell processor (it's a known issue with Dozer - it doesn't support Java 8 time).

    The 2 workarounds are...

    Use CsvBeanReader

    Swap CsvDozerBeanReader out with CsvBeanReader. You'll lose deep/indexed mapping support, but on the plus side it'll be a lot faster.

    Configure Java 8 support in your DozerBeanMapper

    As discussed on the Dozer issue, there is a dozer-jdk8-support library that solves this issue.

    Add the dependency:

    <dependency>
      <groupId>io.craftsman</groupId>
      <artifactId>dozer-jdk8-support</artifactId>
      <version>1.0.2</version>
    </dependency>
    

    Configure a DozerBeanMapper:

    DozerBeanMapper beanMapper = new DozerBeanMapper();
    beanMapper.setMappingFiles(Collections.singletonList("dozerJdk8Converters.xml"));
    

    And supply it to your CsvDozerBeanReader:

    new CsvDozerBeanReader(reader, CsvPreference.STANDARD_PREFERENCE, beanMapper)
    

    It's a little bit of boilerplate, but if you really need Dozer support, this will get you up and running.

    p.s. I've created a PR to have the documentation updated - only one Java 8 cell processor was listed, and there are heaps!