【问题标题】:NoSuchMethodException: java.time.LocalDateTime.<init>() reading CSV using Super CSVNoSuchMethodException: java.time.LocalDateTime.<init>() 使用 Super CSV 读取 CSV
【发布时间】:2017-04-25 11:52:48
【问题描述】:

我使用Super CSV'sICsvDozerBeanWriter 将一个仅包含LocalDateTime 的实体写入CSV 文件,并且在使用ICsvDozerBeanReader 读回它时遇到错误。我能够成功读取和写入 Date 对象,但 LocalDateTime 无法正常工作。

我已经添加了 super-csv-java8 依赖项,并且写作部分似乎工作正常。

我创建了一个小型演示应用程序in this Github repo 来复制问题。运行main()方法,错误会输出到控制台。

这是我得到的例外:

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)

理想情况下,我想以 yyyy-MM-dd 格式将日期写入 CSV 文件,但一次一步!

【问题讨论】:

  • 您应该尝试创建一个minimal reproducible example,不使用 Spring Boot 或任何(很可能)不相关的东西,并将其发布在问题本身中。

标签: java csv java-8 dozer supercsv


【解决方案1】:

虽然 Super CSV 确实 支持通过它的 ParseLocalDateTimeFmtLocalDateTime 单元处理器(均在 super-csv-java8 模块中提供)读取和写入 java.time.LocalDateTime,但 Dozer 正在尝试实例化目标LocalDateTime 对象而不是使用单元处理器的结果(它是带有 Dozer 的 known issue - 它不支持 Java 8 时间)。

两种解决方法是...

使用 CsvBeanReader

CsvDozerBeanReader 替换为CsvBeanReader。您将失去深度/索引映射支持,但从好的方面来说,它会更快。

在您的 DozerBeanMapper 中配置 Java 8 支持

正如在推土机问题上所讨论的,有一个 dozer-jdk8-support 库可以解决这个问题。

添加依赖:

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

配置DozerBeanMapper

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

并将其提供给您的CsvDozerBeanReader

new CsvDozerBeanReader(reader, CsvPreference.STANDARD_PREFERENCE, beanMapper)

这有点像样板,但如果您真的需要 Dozer 支持,这将使您启动并运行。

附言我创建了一个 PR 来更新 documentation - 只列出了一个 Java 8 单元处理器,而且有很多!

【讨论】:

  • 我已将 dozer-jdk8-support 添加到我的项目中,并按照 here 的指示在我的基于 maven 的项目中的 src/main/resources 下添加了 dozerJdk8Converters.xml。但是我仍然遇到与 org.dozer.MappingException: java.lang.NoSuchMethodException: java.time.LocalDateTime.() 相同的错误。知道出了什么问题吗?
【解决方案2】:

LocalDateTime不是片刻

Answer by moldovean 是正确的。 LocalDateTime 是用于某个时间点的错误类。没有时区或与 UTC 偏移的概念,此类只是可能时刻的模糊概念。 在应用时区或偏移之前没有实际意义

自定义单元处理器

我不是SuperCSV 的专家。我找不到完整的文档来讨论它自动处理的数据类型。如果此库尚未更新为直接处理 java.time 类型,您将需要编写自己的映射实现,显然是 custom cell processor

查看this list of built-in cell processors,似乎确实该库尚未针对 java.time 类型进行更新。

我在your example app 中看不到任何映射或自定义单元处理器。

Instant

要表示时间线上的一个时刻,请使用Instant 类。

Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

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

ISO 8601

ISO 8601 标准为序列化为文本的日期时间值定义了多种格式。这些对于跨文化的人类来说是实用、明确、易于解析和直观的。

java.time 类在解析/生成字符串时默认使用这些格式。这包括 Instant 类。

String output = instant.toString();

2016-12-09T22:27:17.783Z

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

除了Instant 之外,您还可以从OffsetDateTime 类和ZonedDateTime 类生成字符串。它们也可以使用 ISO 8601 生成双向数据交换的双向字符串。在ZonedDateTime 的情况下,toString 方法通过在方括号中明智地附加时区名称来扩展 ISO 8601 格式。但通常最好使用 UTC (Instant) 值。

【讨论】:

    【解决方案3】:

    LocalDateTime 是一个不可变对象,并且: 如果没有偏移或时区等附加信息,它就不能代表时间线上的瞬间。

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

    因此,您正在尝试为一开始无法实例化的事物重建实例。

    可能的解决方案:为什么不保存所需日期的字符串表示形式,然后将其解析回 LocalDateTime ?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-21
      • 2016-12-03
      • 2012-12-25
      相关资源
      最近更新 更多