如何为嵌入式对象编写GSON自定义反序列化器,有两种可能的类型

时间:2014-12-23 00:11:33

标签: java json serialization gson

我在尝试反序列化java对象时遇到问题,因为对象内的字段(" info")可能是两种可能的类型之一:ArrayList或者只是一个String.Here就是我这样做的远:

首先,创建类Base:

public class Base {
}

接下来创建子类:

public class GoodInfo extends Base {
    public ArrayList<MyCustomObject> info;
}

public class BadInfo extends Base {
    public String info;
}

所以现在我想解析我的JSON,这是一个Base对象的ArrayList(即每个对象是ArrayList或String的对象的ArrayList):

Type listOfBase = new TypeToken<ArrayList<Base>>(){}.getType();
ArrayList<Base> resp=gson.fromJson(jsonText, listOfBase);

我知道为了实现这一点,我必须编写一个自定义反序列化器。解串器看起来像这样:

private class MyCustomDeserializer implements JsonDeserializer<DateTime> {
    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
  throws JsonParseException {
        // WHAT DO I DO HERE?
    }
}

正如您所看到的,我不知道如何尝试反序列化每个子类型并返回有效的类型。有谁知道怎么做?

我认为它看起来像这样:

private class MyCustomDeserializer implements JsonDeserializer<DateTime> {
    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
  throws JsonParseException {
        try {
            GoodInfo goodInfo=SOMEHOW TRY TO DESERIALIZE json INTO A GoodInfo object
            return goodInfo;
        } catch {
            //
        }
        try {
            BadInfo badInfo=SOMEHOW TRY TO DESERIALIZE json INTO A BadInfo object
            return badInfo;
        } catch {
            throw new JsonParseException("Could not deserialize");
        }
    }
}

根据GSON,我不能在传入的json上使用context.deserialize: 在指定对象上调用默认反序列化。永远不应该在作为JsonDeserializer.deserialize(JsonElement,Type,JsonDeserializationContext)方法的参数接收的元素上调用它。这样做会导致无限循环,因为Gson会再次调用自定义反序列化器。

那我该怎么做?

2 个答案:

答案 0 :(得分:7)

没有documentation reads(强调我的):

  

...你永远不应该在传递json 的相同类型上调用它,因为这会导致无限循环......

只要类型不同,就可以调用context.deserialize(...)

您可以使用json检查info字段并根据元素类型采取相应的操作,而不是在解串器中捕获异常,例如:

public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    final JsonElement elem = json.getAsJsonObject()
                                 .get("info");
    if (elem.isJsonArray()) {
        return context.deserialize(json, GoodInfo.class);
    }
    return context.deserialize(json, BadInfo.class);
}

或者,您可以通过修改超类来完全绕过自定义JsonDeserializer。将info字段作为Object提取,例如:

public class Base {
    public Object info;
}

允许Gson适当地反序列化该值。

答案 1 :(得分:4)

class MyCustomDeserializer implements JsonDeserializer<Base>
{
    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
    {
        try
        {
            return context.deserialize(json, GoodInfo.class);
        }
        catch (JsonParseException e)
        {
            return context.deserialize(json, BadInfo.class);
        }
    }
}

此解决方案工作正常,因为GoodInfoBadInfo与GSON的Base类型不同,因为从对象的角度来看,GoodInfoBadInfo个实例 Base个实例。

考虑到这两个类已实现toString()

class GoodInfo extends Base
{
    public ArrayList<String> info;

    public String toString()
    {
        return info.toString();
    }
}

class BadInfo extends Base
{
    public String info;

    public String toString()
    {
        return info;
    }
}

测试:

Type listOfBase = new TypeToken<ArrayList<Base>>(){}.getType();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Base.class, new MyCustomDeserializer());
Gson gson = gsonBuilder.create();
ArrayList<Base> resp = gson.fromJson("[{\"info\": \"test\"}, {\"info\": [\"test\", \"test\", \"test\"]}]", listOfBase);
System.out.println(resp.size());
for (Object o : resp) System.out.println(o);

打印:

2 test [test, test, test]