如果我使用界面或抽象类注册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
调用序列化程序。
对我来说,这是一种不一致的行为。它对成员变量的工作方式是查找声明的类型而不是实际类型,但它对“根”对象的工作方式是不同的。
这是错误/缺陷还是预期的行为?如果这是预期的,背后的任何原因?
答案 0 :(得分:9)
这是预期的行为。 Gson有一个默认的TypeAdapterFactory
对象列表。您可以在source of the Gson class。
有适用于String
,Number
,Map
,Collection
的适配器......最后一个很重要,在没有适配器可以处理类型时使用:它是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());
希望有所帮助