Gson串行化接口/抽象类

hmae6n7t  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(189)

如果我用一个接口抽象类注册一个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

调用序列化程序。
对我来说,这是一种不一致的行为。它对成员变量的工作方式是寻找 * 声明的 * 类型而不是 * 实际的 * 类型,但它对“根”对象的工作方式是不同的。
这是Bug /缺陷还是预期行为?如果是预期行为,有什么原因吗?

b4qexyjb

b4qexyjb1#

这是预期的行为。Gson有一个默认TypeAdapterFactory对象的列表可以使用。你可以在Gson类的源代码中看到它们。
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(对象,类型)

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

2)注册适配器时,可以指明此适配器适用于所有子类:GsonBuilder#注册表类型层次结构树

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

希望能有所帮助

相关问题