简体   繁体   中英

Gson Failing to call custom Serializer

I've been trying to follow the advice given here to turn off scientific notation on numeric values represented in Json. The problem I've got is that my custom Serializer is never called.

I've tried different variations of the code and have eventually ended up with:

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));
    }
}

If I've understood the API correctly then this code use the custom serialisation for all values so it should generate "xxx" for all values, but what I keep getting is:

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

What's going wrong?

In playwright, a microsoft library, and rust-rcon library, something similar happened to me. I leave you link.

This error occurs because you have installed jdk 11 or upper and a gson prior to 2.8.6

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

The solution was to go to the latest version of gson, although the version was the one they used, I added it to my POM to force maven to make the rest of the dependencies use the latest version. Try to see and tell me!

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

Try this solution:D

What's going wrong?

Nothing wrong because of the limitations Gson has by design: Object and JsonElement type adapter hierarchies cannot be overridden.

Here is the test covering all four object/number hierarchy and value/JSON tree pairs:

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));
    }

}

In short JsonElement s are considered already-serialized, so what you're looking for is hidden in testNumberObject : define Number as a superclass (or Float / Double to be most precise), and serialize an object containing fields, not JsonElement . If you must use JsonElement , then put a "good-formattible" value right into the Num property ( BigDecimal should work just fine).


Update 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));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM