Gson 序列化接口/抽象class

Gson serialize interface / abstract class

如果我使用 接口 抽象 class 注册一个 JsonSerializer,例如:

GsonBuilder builder = new GsonBuilder()
    .registerTypeAdapter(MyInterface.class, new MySerializer());
Gson gson = builder.create();

MyInterface i = new MyConcreteClass();
String json = gson.toJson(i); // <--- Serializer is not invoked

gson.toJson().

中根本没有调用序列化程序

但是,如果我有一个包含 MyInterface 的 class:

public class MyAnotherClass {
    MyInterface i;
    // ...
}

MyAnotherClass c = new MyAnotherClass()
String json = gson.toJson(c); // <--- Serializer is invoked

序列化程序被调用。

对我来说,这是一种不一致的行为。它在成员变量上的工作方式是寻找 declared 类型而不是 actual 类型,但它在 [=36= 上工作的方式] 对象不同。

这是错误/缺陷还是预期行为?如果这是预料之中的,背后有什么原因吗?

这是预期的行为。 Gson 有一个要使用的默认 TypeAdapterFactory 对象列表。您可以在 source of the Gson class.

中看到它们

StringNumberMapCollection的适配器...最后一个很重要,当没有适配器可以处理类型时使用: 就是ReflectiveTypeAdapter。这一个通过反射检索要序列化的对象的所有字段,并搜索适当的适配器来序列化字段的值。

所以当 Gson 必须序列化一个对象时,首先它会尝试使用对象的实际类型来查找适配器。如果找到的适配器不是 ReflectiveTypeAdapter,它会使用它。否则它会搜索具有声明类型的适配器,如果它不是 ReflectiveTypeAdapter,则使用它。否则它会在对象上使用 ReflectiveTypeAdapter(您可以查看 class TypeAdapterRuntimeTypeWrapper 的来源以了解有关此机制的更多详细信息)。这就是您的属性 MyInterface i.

的行为

当你这样做时:

MyInterface i = new MyConcreteClass();
String json = gson.toJson(i);

Gson 尝试为类型 MyConcreteClass 找到一个适配器,它找到了 ReflectiveTypeAdapter。所以现在它应该搜索具有声明类型的适配器。但它无法知道声明类型,因为没有对象引用它,这是根对象。你知道在你的代码中你想要使用 MyInterface 类型,但是 Gson 没有这个信息并且只有对象实例。你可能认为 Gson 可以看到对象实现了 MyInterface,但这不是它的工作方式(如果对象实现了两个接口怎么办?)

所以,您有两种方法可以解决您的问题:

1) 调用Gson.toJson时,可以给根对象声明类型:Gson#toJson(Object, Type)

String json = gson.toJson(i, MyInterface.class);

2) 当你注册你的适配器时,你可以指明这个适配器适用于所有子classes:GsonBuilder#registerTypeHierarchyTree

GsonBuilder builder = new GsonBuilder()
    .registerTypeHierarchyAdapter(MyInterface.class, new MySerializer());

希望对您有所帮助