Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registering a deserializer changes behavior of serialization #2032

Closed
1 of 4 tasks
JohnnyJayJay opened this issue Dec 13, 2021 · 3 comments · Fixed by #1787
Closed
1 of 4 tasks

Registering a deserializer changes behavior of serialization #2032

JohnnyJayJay opened this issue Dec 13, 2021 · 3 comments · Fixed by #1787
Labels

Comments

@JohnnyJayJay
Copy link

Gson version

2.8.9

Java / Android version

Java SE 17 (OpenJDK)

Used tools

  • Maven; version:
  • Gradle; version: 7.3.1
  • ProGuard (attach the configuration file please); version:
  • ...

Description

Serialisation behaviour changes based on whether a custom deserialiser is set or not. More specifically, my objects are not serialised in their entirety if a custom deserialiser is provided.

Here is a case to illustrate:

public static abstract class A {
    private String x = "hello";
}
public static class B extends A {
    private int y = 12;
}
public static class C extends A {
    private double z = 231.2;
}
public static class Foo {
    private A a;
}

Creating a Foo object, assigning its a field to new B() or new C() results in the intuitive behaviour when serialising the Foo object:

{
  "a": {
    "y": 12,
    "x": "hello"
  }
}

However, when a custom deserialiser for A is set using registerTypeAdapter, this changes to

{
  "a": {
    "x": "hello"
  }
}

Expected behavior

I expect the behaviour to stay the same, whether a custom deserialiser is registered or not.

Actual behavior

The behaviour changes and fields of sub classes are omitted in the result.

Reproduction steps

public class Demo {
    static void caseOne() {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        Foo src = new Foo();
        src.a = new B();
        String out = gson.toJson(src);
        System.out.println(out);
    }
    static void caseTwo() {
        JsonDeserializer<A> deserializer = (elem, t, c) -> new B();
        Gson gson = new GsonBuilder().setPrettyPrinting()
                .registerTypeAdapter(A.class, deserializer)
                .create();
        Foo src = new Foo();
        src.a = new B();
        String out = gson.toJson(src);
        System.out.println(out);
    }
    public static abstract class A {
        private String x = "hello";
    }
    public static class B extends A {
        private int y = 12;
    }
    public static class C extends A {
        private double z = 231.2;
    }
    public static class Foo {
        private A a;
    }
}

The above class can be used to reproduce this behaviour.

Exception stack trace

/

Additional notes

This issue seems similar to #1599 , however I am not satisfied with the answer provided there. If the intent behind this behaviour was to be able to guarantee that any serialised object can be deserialised again, why does caseOne work although the result can obviously not be deserialised using Gson defaults anymore?

I am also aware that using registerTypeHierarchyAdapter instead "fixes" the problem. However I don't consider this a fix because it changes the entire semantics of the custom deserialiser. For instance, I can't use the deserialisation context anymore to delegate deserialisation. E.g. context.deserialize(something, B.class) doesn't work anymore because it's recursive.

@Marcono1234
Copy link
Collaborator

The underlying issue seems to be the one fixed by #1787.

You could probably work around this by writing a TypeAdapterFactory which obtains the delegate TypeAdapter (which is reflection based). Then for serialization it uses the delegate adapter, but for deserialization it performs your custom deserialization logic.


Note that I am not a member of this project.

@JohnnyJayJay
Copy link
Author

It seems like that is indeed related to that PR. TypeAdapterFactory is a good enough workaround for the time being. It's a shame apparently nobody has looked at that PR though, this would be nice to fix.
Thanks a lot.

@eamonnmcmanus
Copy link
Member

I'm looking into whether we can merge #1787.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants