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.
中看到它们
有String
、Number
、Map
、Collection
的适配器...最后一个很重要,当没有适配器可以处理类型时使用: 就是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());
希望对您有所帮助
如果我使用 接口 或 抽象 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.
有String
、Number
、Map
、Collection
的适配器...最后一个很重要,当没有适配器可以处理类型时使用: 就是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());
希望对您有所帮助