Gson的意外行为

sxissh06  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(211)

我开发了一个小型应用程序,用于存储来自设备的数据:我选择以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

收到答案后,我在这里打开了一个问题。

az31mfrm

az31mfrm1#

TL;DR

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

完整答案

GSON希望创建它反序列化的类的示例,而不调用任何构造函数(所以没有GSON的命令,什么都不会被初始化)。这通常是不可能完成的,但是sun.misc.Unsafe提供了一种方法,可以通过allocateInstance来完成。为此,GSON需要一个sun.misc.Unsafe的示例。调用栈中最上面的帧来自UnsafeAllocator,其使用普通的技巧得到Unsafe
问题是,sun.misc.Unsafe在模块 jdk.unsupported 中,它存在于完整的JDK中,但通常不会在运行时映像中找到。
当使用jlink创建运行时映像时,请确保包含选项--add-modules jdk.unsupported,这样您就可以开始了。
可以说,GSON应该在 jdk.unsupported 上声明一个optional dependency,其中包含requires static

相关问题