Java 反序列化 Gson 中的抽象类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3629596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Deserializing an abstract class in Gson
提问by user437480
I have a tree object in JSON format I'm trying to deserialize with Gson. Each node contains its child nodes as fields of object type Node. Node is an interface, which has several concrete class implementations. During the deserialization process, how can I communicate to Gson which concrete class to implement when deserializing the node, if I do not know a priori which type the node belongs to? Each Node has a member field specifying the type. Is there a way to access the field when the object is in serialized form, and somehow communicate the type to Gson?
我有一个 JSON 格式的树对象,我正在尝试使用 Gson 反序列化。每个节点都包含其子节点作为对象类型节点的字段。Node 是一个接口,它有几个具体的类实现。在反序列化过程中,如果我不知道节点属于哪种类型的先验,我如何与 Gson 通信在反序列化节点时要实现的具体类?每个节点都有一个成员字段指定类型。当对象处于序列化形式时,有没有办法访问该字段,并以某种方式将类型传达给 Gson?
Thanks!
谢谢!
采纳答案by ColinD
I'd suggest adding a custom JsonDeserializerfor Node
s:
我建议为s添加一个自定义的JsonDeserializerNode
:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Node.class, new NodeDeserializer())
.create();
You will be able to access the JsonElement
representing the node in the deserializer's method, convert that to a JsonObject
, and retrieve the field that specifies the type. You can then create an instance of the correct type of Node
based on that.
您将能够访问JsonElement
反序列化器方法中的表示节点,将其转换为 a JsonObject
,并检索指定类型的字段。然后,您可以基于此创建正确类型的实例Node
。
回答by Guruprasad GV
You will need to register both JSONSerializer and JSONDeserializer. Also you can implement a generic adapter for all your interfaces in the following way:
您将需要同时注册 JSONSerializer 和 JSONDeserializer。您还可以通过以下方式为所有接口实现通用适配器:
- During Serialization : Add a META-info of the actual impl class type.
- During DeSerialization : Retrieve that meta info and call the JSONDeserailize of that class
- 在序列化期间:添加实际 impl 类类型的 META-info。
- 在 DeSerialization 期间:检索该元信息并调用该类的 JSONDeserailize
Here is the implementation that I have used for myself and works fine.
这是我自己使用的实现并且工作正常。
public class PropertyBasedInterfaceMarshal implements
JsonSerializer<Object>, JsonDeserializer<Object> {
private static final String CLASS_META_KEY = "CLASS_META_KEY";
@Override
public Object deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
JsonObject jsonObj = jsonElement.getAsJsonObject();
String className = jsonObj.get(CLASS_META_KEY).getAsString();
try {
Class<?> clz = Class.forName(className);
return jsonDeserializationContext.deserialize(jsonElement, clz);
} catch (ClassNotFoundException e) {
throw new JsonParseException(e);
}
}
@Override
public JsonElement serialize(Object object, Type type,
JsonSerializationContext jsonSerializationContext) {
JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass());
jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY,
object.getClass().getCanonicalName());
return jsonEle;
}
}
Then you could register this adapter for all your interfaces as follows
然后你可以为你的所有接口注册这个适配器,如下所示
Gson gson = new GsonBuilder()
.registerTypeAdapter(IInterfaceOne.class,
new PropertyBasedInterfaceMarshal())
.registerTypeAdapter(IInterfaceTwo.class,
new PropertyBasedInterfaceMarshal()).create();
回答by Jason Polites
As far as I can tell this doesn't work for non-collection types, or more specifically, situations where the concrete type is used to serialize, and the interface type is used to deserialize. That is, if you have a simple class implementing an interface and you serialize the concrete class, then specify the interface to deserialize, you'll end up in an unrecoverable situation.
据我所知,这不适用于非集合类型,或者更具体地说,具体类型用于序列化,接口类型用于反序列化的情况。也就是说,如果您有一个实现接口的简单类并序列化具体类,然后指定要反序列化的接口,您最终将处于不可恢复的情况。
In the above example the type adapter is registered against the interface, but when you serialize using the concrete class it will not be used, meaning the CLASS_META_KEY data will never be set.
在上面的例子中,类型适配器是针对接口注册的,但是当您使用具体类进行序列化时,它将不会被使用,这意味着永远不会设置 CLASS_META_KEY 数据。
If you specify the adapter as a hierarchical adapter (thereby telling gson to use it for all types in the hierarchy), you'll end up in an infinite loop as the serializer will just keep calling itself.
如果您将适配器指定为分层适配器(从而告诉 gson 将它用于层次结构中的所有类型),您将最终陷入无限循环,因为序列化程序将继续调用自己。
Anyone know how to serialize from a concrete implementation of an interface, then deserialize using only the interface and an InstanceCreator?
任何人都知道如何从接口的具体实现序列化,然后仅使用接口和 InstanceCreator 进行反序列化?
By default it seems that gson will create the concrete instance, but does not set it's fields.
默认情况下,gson 似乎会创建具体实例,但不会设置它的字段。
Issue is logged here:
问题记录在这里:
http://code.google.com/p/google-gson/issues/detail?id=411&q=interface
http://code.google.com/p/google-gson/issues/detail?id=411&q=interface
回答by paul
You have to use TypeToken
class from Google Gson.
You will need of course has a generic class T to make it works
您必须使用TypeToken
Google Gson 中的类。您当然需要有一个通用类 T 才能使其工作
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);