Gson的意外行为

问题描述 投票:2回答:1

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

我想我遇到的问题是自定义类型,而不是内置类型。这时,我发现了两件事。

  • 如果我使用完整的JRE9来运行这个jar,就不会抛出异常:我仔细检查了我用Jlink.exe创建的自定义JRE中包含的模块,所有的东西都正确地包含了。我仍然想使用一个更小的JRE,所以我还没有进一步调查(我想这解释了为什么在IDE中它能完美地工作)。
  • 我在GSON对象中添加了一个自定义的解串器(见下图),我只需手动将JSON字符串转换为有效的数据,这样就避免了LocalDateTime类的异常......但异常再次出现在另一个类上,这次是一个自定义的类。

在这一点上,我想我可以简单的为每个引起问题的数据类型添加一个反序列器,但是我想知道 为什么在使用完整的JRE时不会发生这个问题,为什么在使用较小的JRE时就会发生这个问题即使所有需要的模块都包含在内。也许值得一提的是,我没有在保存数据的Gson对象中添加自定义序列化器,而是按照GSON的默认值进行序列化。

LocalDateTime deserializer。

    @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

EDIT:在我收到答案后,我开了一个问题 此处

json gson java-9 jlink jdeps
1个回答
6
投票

TL;DR

你需要一个运行时映像(例如完整的JDK或用jlink构建的东西),其中包括模块 jdk.unsupported.

完整答案

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.unsupportedrequires static.

© www.soinside.com 2019 - 2024. All rights reserved.