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

12,940

Solution 1

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!

Solution 2

LocalDateTime is an immutable object and: It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html

So you are trying to reconstruct an instance for something that cannot be instantiated in the first place.

possible solution: Why don't you save a string representation of the date you need and then parse it back to a LocalDateTime ?

Solution 3

LocalDateTime is not a moment

The Answer by moldovean is correct. LocalDateTime is the wrong class to use for a point in time. Having no concept of time zone or offset-from-UTC, this class is only a vague idea of possible moments. Has no real meaning until you apply a time zone or offset.

Custom cell processor

I'm no expert on SuperCSV. I cannot find full documentation discussing what data types it handles automatically. If this library has not yet been updated to handle the java.time types directly, you will need to write your own mapping implementation, apparently a custom cell processor.

Looking at this list of built-in cell processors, it seems that indeed the library has not yet been updated for java.time types.

I see no mapping or custom cell processor in your example app.

Instant

To represent a moment, a point on the timeline, use the Instant class.

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = Instant.now();  // Current moment in UTC.

ISO 8601

The ISO 8601 standard defines many formats for date-time values being serialized as text. These are practical, unambiguous, easy to parse, and intuitive for humans across cultures.

The java.time classes use these formats by default when parsing/generating strings. This includes the Instant class.

String output = instant.toString();

2016-12-09T22:27:17.783Z

Instant instant = Instant.parse( "2016-12-09T22:27:17.783Z" );

You can also generate strings from the OffsetDateTime class and ZonedDateTime class in addition to Instant. They too can generate bi-strings for bi-directional data exchange using ISO 8601. In the case of ZonedDateTime the toString method extends the ISO 8601 format by wisely appending the name of the time zone in square brackets. But generally best to work with UTC (Instant) values.

Share:
12,940
Stuart Leyland-Cole
Author by

Stuart Leyland-Cole

Updated on June 11, 2022

Comments

  • Stuart Leyland-Cole
    Stuart Leyland-Cole about 2 years

    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!

  • calvin.mk
    calvin.mk over 5 years
    I have added the dozer-jdk8-support to my project and added dozerJdk8Converters.xml under src/main/resources in my maven based project, as directed here. But am still getting same error that of org.dozer.MappingException: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>(). Any idea what's going wrong ?