繁体   English   中英

在 Gson 中反序列化一个摘要 class

[英]Deserializing an abstract class in Gson

我有一个 JSON 格式的树 object 我正在尝试用 Gson 反序列化。每个节点都包含其子节点作为 object 类型节点的字段。 Node是一个接口,它有几个具体的class实现。 在反序列化过程中,如果我先验不知道该节点属于哪种类型,我如何在反序列化节点时与 Gson 通信具体实现哪个 class ? 每个节点都有一个指定类型的成员字段。 当 object 为序列化形式时,有没有办法访问该字段,并以某种方式将类型传递给 Gson?

谢谢!

我建议为Node添加一个自定义的JsonDeserializer

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

您将能够访问表示反序列化器方法中的节点的JsonElement ,将其转换为JsonObject ,并检索指定类型的字段。 然后,您可以基于此创建正确类型的Node的实例。

您需要注册JSONSerializer和JSONDeserializer。 您还可以通过以下方式为所有接口实现通用适配器:

  • 在序列化期间:添加实际impl类类型的META-info。
  • 在DeSerialization期间:检索该元信息并调用该类的JSONDeserailize

这是我自己使用的实现,并且工作正常。

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;
    }

}

然后,您可以为所有接口注册此适配器,如下所示

Gson gson = new GsonBuilder()
        .registerTypeAdapter(IInterfaceOne.class,
                new PropertyBasedInterfaceMarshal())
        .registerTypeAdapter(IInterfaceTwo.class,
                new PropertyBasedInterfaceMarshal()).create();

我想稍微纠正一下上面的内容

public class PropertyMarshallerAbstractTask implements JsonSerializer<Object>, JsonDeserializer<Object> {

    private static final String CLASS_TYPE = "CLASS_TYPE";

    @Override
    public Object deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {

        JsonObject jsonObj = jsonElement.getAsJsonObject();
       String className = jsonObj.get(CLASS_TYPE).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) {
    
        Gson gson = new Gson(); //without this line it will not work
        gson.toJson(object, object.getClass()); //and this one
        JsonElement jsonElement = gson.toJsonTree(object); //it needs to replace to another method...toJsonTree
        jsonElement.getAsJsonObject().addProperty(CLASS_TYPE, object.getClass().getCanonicalName());
        return jsonElement;
    }
}

然后我使用它:

Gson gson = new GsonBuilder()
                .registerTypeAdapter(AbstractTask.class, new PropertyMarshallerOfAbstractTask())
                .create();

然后我可以将 List(我保留一些非抽象类,它们继承自 Abstract Task)解析为 Json;

它在相反的方向起作用

List<AbstractTask> abstractTasks = gson.fromJson(json, new TypeToken<List<AbstractTask>>(){}.getType());

据我所知,这对非集合类型不起作用,或者更具体地说,对于使用具体类型进行序列化的情况,并且接口类型用于反序列化。 也就是说,如果你有一个实现接口的简单类并且序列化具体类,那么指定要反序列化的接口,你将最终处于不可恢复的状态。

在上面的示例中,类型适配器是针对接口注册的,但是当您使用具体类进行序列化时,它将不会被使用,这意味着将永远不会设置CLASS_META_KEY数据。

如果您将适配器指定为分层适配器(从而告诉gson将其用于层次结构中的所有类型),您将最终处于无限循环中,因为序列化程序将继续调用自身。

任何人都知道如何从接口的具体实现序列化,然后只使用接口和InstanceCreator反序列化?

默认情况下,gson似乎会创建具体实例,但不会设置它的字段。

问题记录在这里:

http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

您必须使用Google Gson中的TypeToken类。 你当然需要一个通用的类T来使它工作

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

gson.toJson(foo,fooType);

gson.fromJson(json, fooType);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM