【问题标题】:Unexpected behaviour from GsonGson 的意外行为
【发布时间】:2020-05-11 10:33:15
【问题描述】:

我开发了一个存储来自设备的数据的小型应用程序:我选择以 JSON 格式存储数据,并且数据的序列化/反序列化工作得很好,即使它涉及我创建的一些自定义类型......但只有我在 IDE 中工作(Eclipse,就此而言)。

但是,当我导出一个可运行的 JAR 文件时,数据的反序列化遇到了某种问题,因为软件总是抛出这个异常:

Caused by: java.lang.UnsupportedOperationException: Cannot allocate class LocalDateTime
    at com.google.gson.internal.UnsafeAllocator$4.newInstance(UnsafeAllocator.java:104)
    at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
    ... 88 common frames omitted

我以为我会遇到自定义类型的问题,而不是内置类型。至此,我发现了两件事:

  • 如果我使用完整的 JRE 9 运行 JAR 文件,则不会引发异常:我仔细检查了使用 Jlink.exe 创建的自定义 JRE 中包含的模块,并且所有内容都正确包含。我仍然想使用更小的 JRE,所以我还没有进一步调查(我想这解释了为什么它在 IDE 中完美运行)
  • 我为 Gson 对象添加了一个自定义反序列化程序(见下文),我只是手动将 JSON 字符串转换为有效数据,从而避免了 LocalDateTime 类上的异常...但异常再次出现在另一个课程,这次是定制课程。

此时,我想我可以简单地为导致问题的每种数据类型添加一个反序列化器,但我想知道 为什么完整的 JRE 不会出现问题,以及为什么较小的 JRE 会导致这个问题,即使包含了所有必需的模块。也许值得一提的是,我没有为保存数据的 Gson 对象添加自定义序列化程序,它全部按照 Gson 默认进行序列化。

LocalDateTime解串器:

    @Override
    public LocalDateTime deserialize(JsonElement json, java.lang.reflect.Type type,
                JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {

        JsonObject joDate = json.getAsJsonObject().get("date").getAsJsonObject();
        JsonObject joTime = json.getAsJsonObject().get("time").getAsJsonObject();
        //JSON example: {"date":{"year":2019,"month":1,"day":9},"time":{"hour":6,"minute":14,"second":1,"nano":0}
        return LocalDateTime.of(joDate.get("year").getAsInt(),
                joDate.get("month").getAsInt(),
                joDate.get("day").getAsInt(),
                joTime.get("hour").getAsInt(),
                joTime.get("minute").getAsInt(),
                joTime.get("second").getAsInt(),
                joTime.get("nano").getAsInt());
    }
}

Jdeps.deps 模块列表:

com.google.gson
java.base
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
org.slf4j

收到答复后,我打开了一个问题here

【问题讨论】:

  • 在开始之前,如果您可以使项目可用,那将使调试变得容易得多。 :)
  • @Nicolai 我没有添加更多代码,因为该问题似乎与 LocalDateTime 类的通用序列化/反序列化有关,而且我的代码似乎完全不相关,因为它仅使用 GSON 的默认设置

标签: json gson java-9 jlink jdeps


【解决方案1】:

TL;DR

您需要一个包含模块 jdk.unsupported 的运行时映像(例如完整的 JDK 或使用 jlink 构建的东西)。

完整答案

GSON 希望在不调用任何构造函数的情况下创建它反序列化的类的实例(所以如果没有 GSON 这么说,什么都不会被初始化)。这通常无法完成,但sun.misc.Unsafe 提供了一种使用方法allocateInstance 执行此操作的方法。为此,GSON 需要一个 sun.misc.Unsafe 的实例。调用堆栈中最顶层的帧来自UnsafeAllocator,它使用common trickery to get Unsafe

问题是,sun.misc.Unsafe 位于模块 jdk.unsupported 中,该模块存在于完整的 JDK 中,但通常不会在运行时映像中找到。

使用 jlink 创建运行时映像时,请确保包含选项 --add-modules jdk.unsupported,您应该一切顺利。

可以说,GSON 应该在 jdk.unsupported 上用requires static 声明一个optional dependency

【讨论】:

  • 花大力气调试问题+1
  • 它成功了,我刚刚创建了一个新的 JRE,包括 jdk.unsupported 并且 GSON 没有抛出任何异常。无论如何,仅针对我在问题中提到的两个类引发了异常,因此由两个反序列化器解决了。我应该将此可选依赖项通知 GSON 开发人员吗?
  • 很高兴我能帮上忙。是的,打开一个问题是个好主意,尽管有一个重要的细节需要讨论——不过那里比这里更好。问题打开后请留下链接。
  • 为什么对jdk.unsupported的依赖是可选的?常规依赖不是更好吗,因为显然没有Unsafe,Gson 的功能是有限的。
  • 这应该在 GSON 问题上讨论。简短回答:不应依赖 jdk.unsupported 的存在(注意它的名称),并且由于 GSON 没有它也可以工作(只是不太舒服),因此避免严格依赖将是 IMO 的一个不错的选择。
猜你喜欢
  • 1970-01-01
  • 2020-10-04
  • 2016-07-16
  • 2016-05-10
  • 2020-07-23
  • 2021-08-23
  • 2021-11-16
  • 2017-10-20
  • 2016-06-29
相关资源
最近更新 更多