Gson 无法调用自定义序列化程序

Gson Failing to call custom Serializer

我一直在尝试按照 here 给出的建议关闭 Json 中表示的数值的科学记数法。我遇到的问题是我的自定义序列化程序从未被调用过。

我尝试了不同的代码变体,最终得到了:

public class TestExternaliser {
    static class SpecialSerializer implements JsonSerializer<Object> {
        @Override
        public JsonElement serialize(Object x,
                                     Type type,
                                     JsonSerializationContext jsonSerializationContext) {
            return new JsonPrimitive("xxx");
        }
    }

    public static void main(String... args) {
        JsonObject root = new JsonObject();

        root.addProperty("String", "String");
        root.addProperty("Num", Integer.valueOf(123));
        root.addProperty("Bool", Boolean.TRUE);

        Gson gson = new GsonBuilder()
                .registerTypeHierarchyAdapter(Object.class, new SpecialSerializer())
                .setPrettyPrinting()
                .create();

        System.out.println(gson.toJson(root));
    }
}

如果我对 API 的理解正确,那么此代码会对所有值使用自定义序列化,因此它应该为所有值生成 "xxx",但我一直得到的是:

{
  "String": "String",
  "Num": 123,
  "Bool": true
}

怎么了?

在 playwright、一个微软库和 rust-rcon 库中,类似的事情发生在我身上。我离开你link.

出现此错误是因为您安装了jdk 11 或更高版本以及 2.8.6

之前的 gson

https://github.com/microsoft/playwright-java/issues/245#issuecomment-775351308 https://github.com/MrGraversen/rust-rcon/pull/2#event-4300625968

解决方案是使用最新版本的 gson,尽管该版本是他们使用的版本,但我将其添加到我的 POM 中以强制 maven 使其余依赖项使用最新版本。试试看告诉我!

<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
   <version>2.8.6</version>
</dependency>

试试这个解决方案 :D

What's going wrong?

由于 Gson 的设计限制,没有错:ObjectJsonElement 类型适配器层次结构 cannot 被覆盖。

这是涵盖所有四个 object/number 层次结构和 value/JSON 树对的测试:

public final class LimitationsTest {

    private static final JsonSerializer<Object> defaultJsonSerializer = (src, typeOfSrc, context) -> new JsonPrimitive("xxx");

    private static final Gson objectDefaultsGson = new GsonBuilder()
            .registerTypeHierarchyAdapter(Object.class, defaultJsonSerializer)
            .create();

    private static final Gson numberDefaultsGson = new GsonBuilder()
            .registerTypeHierarchyAdapter(Number.class, defaultJsonSerializer)
            .create();

    private static final class Value {
        @SerializedName("String")
        private String string;
        @SerializedName("Num")
        private Number num;
        @SerializedName("Bool")
        private Boolean bool;
    }

    private static final Object object;
    private static final JsonElement jsonElement;

    static {
        final Value newObject = new Value();
        newObject.string = "String";
        newObject.num = 123;
        newObject.bool = Boolean.TRUE;
        object = newObject;
        final JsonObject newJsonElement = new JsonObject();
        newJsonElement.addProperty("String", "String");
        newJsonElement.addProperty("Num", 123);
        newJsonElement.addProperty("Bool", Boolean.TRUE);
        jsonElement = newJsonElement;
    }

    @Test
    public void testObjectObject() {
        Assertions.assertEquals("\"xxx\"", objectDefaultsGson.toJson(object));
    }

    @Test
    public void testObjectJsonElement() {
        Assertions.assertEquals("{\"String\":\"String\",\"Num\":123,\"Bool\":true}", objectDefaultsGson.toJson(jsonElement));
    }

    @Test
    public void testNumberObject() {
        Assertions.assertEquals("{\"String\":\"String\",\"Num\":\"xxx\",\"Bool\":true}", numberDefaultsGson.toJson(object));
    }

    @Test
    public void testNumberJsonElement() {
        Assertions.assertEquals("{\"String\":\"String\",\"Num\":123,\"Bool\":true}", numberDefaultsGson.toJson(jsonElement));
    }

}

简而言之JsonElements被认为是已经序列化的,所以你要找的东西隐藏在testNumberObject中:将Number定义为超类(或Float/Double 最精确),并序列化包含字段的对象,而不是 JsonElement。如果您必须使用 JsonElement,则将“格式良好”的值直接放入 Num 属性(BigDecimal 应该可以正常工作)。


更新 1.

@Test
public void testNoScientificNotationForJsonElement() {
    final JsonObject newJsonElement = new JsonObject();
    newJsonElement.addProperty("a", new BigDecimal(new BigDecimal("1E+10").toPlainString()));
    newJsonElement.addProperty("b", new BigDecimal("1E+10") {
        @Override
        public String toString() {
            return toPlainString();
        }
    });
    final Gson gson = new Gson();
    Assertions.assertEquals("{\"a\":10000000000,\"b\":10000000000}", gson.toJson(newJsonElement));
}