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

3

8

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!

Tulley answered 9/12, 2016 at 22:37 Comment(1)
You should try to create a minimal reproducible example, without Spring Boot or anything (very probably) irrelevant, and post that, in the question itself.Hexachlorophene
H
11

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!

Hindoo answered 10/12, 2016 at 22:54 Comment(1)
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 ?Trodden
S
3

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 ?

Sandhi answered 9/12, 2016 at 22:54 Comment(0)
F
1

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.

Fortunate answered 10/12, 2016 at 0:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.