Gson序列化接口/抽象类

时间:2015-02-06 09:14:48

标签: java gson serializer

如果我使用界面抽象类注册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

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

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

调用序列化程序。

对我来说,这是一种不一致的行为。它对成员变量的工作方式是查找声明的类型而不是实际类型,但它对“根”对象的工作方式是不同的。

这是错误/缺陷还是预期的行为?如果这是预期的,背后的任何原因?

1 个答案:

答案 0 :(得分:9)

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

中查看它们

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

因此,当Gson必须序列化对象时,首先它会尝试使用对象的实际类型来查找适配器。如果找到的适配器不是ReflectiveTypeAdapter,则使用它。否则,它会搜索具有声明类型的适配器,如果它不是ReflectiveTypeAdapter则使用它。否则它会在对象上使用ReflectiveTypeAdapter(您可以查看类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)注册适配器时,可以指示此适配器适用于所有子类:GsonBuilder#registerTypeHierarchyTree

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

希望有所帮助