在Gson中反序列化抽象类

时间:2021-08-16 17:22:15

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!

谢谢!

4 个解决方案

#1


42  

I'd suggest adding a custom JsonDeserializer for Nodes:

我建议为节点添加一个自定义的JsonDeserializer:

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,将其转换为JsonObject,并检索指定类型的字段。然后,您可以基于此创建一个正确类型的节点的实例。

#2


23  

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.
  • 在序列化期间:添加实际impl类类型的元信息。
  • During DeSerialization : Retrieve that meta info and call the JSONDeserailize of that class
  • 在反序列化期间:检索元信息并调用该类的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();

#3


1  

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

#4


0  

You have to use TypeToken class from Google Gson. You will need of course has a generic class T to make it works

您必须使用谷歌Gson中的TypeToken类。当然,您需要一个通用类T来让它工作

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

gson.toJson(foo, fooType);

gson。toJson(foo,fooType);

gson.fromJson(json, fooType);

#1


42  

I'd suggest adding a custom JsonDeserializer for Nodes:

我建议为节点添加一个自定义的JsonDeserializer:

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,将其转换为JsonObject,并检索指定类型的字段。然后,您可以基于此创建一个正确类型的节点的实例。

#2


23  

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.
  • 在序列化期间:添加实际impl类类型的元信息。
  • During DeSerialization : Retrieve that meta info and call the JSONDeserailize of that class
  • 在反序列化期间:检索元信息并调用该类的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();

#3


1  

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

#4


0  

You have to use TypeToken class from Google Gson. You will need of course has a generic class T to make it works

您必须使用谷歌Gson中的TypeToken类。当然,您需要一个通用类T来让它工作

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

gson.toJson(foo, fooType);

gson。toJson(foo,fooType);

gson.fromJson(json, fooType);