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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 02:56:03  来源:igfitidea点击:

Deserializing an abstract class in Gson

javajsonabstract-classgson

提问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 Nodes:

我建议为s添加一个自定义的JsonDeserializerNode

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Node.class, new NodeDeserializer())
    .create();

You will be able to access the JsonElementrepresenting 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 Nodebased 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 TypeTokenclass from Google Gson. You will need of course has a generic class T to make it works

您必须使用TypeTokenGoogle Gson 中的类。您当然需要有一个通用类 T 才能使其工作

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();

gson.toJson(foo, fooType);

gson.toJson(foo, fooType);

gson.fromJson(json, fooType);