-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
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.