簡體   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