如何使用gson获得嵌套的JSON对象?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (11)

我从我的android应用程序使用API​​,所有的JSON响应如下所示:

{
    'status': 'OK',
    'reason': 'Everything was fine',
    'content': {
         < some data here >
}

问题是,我所有的POJO有statusreason字段,里面content领域是真正的POJO我想要的。

有没有什么办法可以创建Gson的自定义转换器来提取content字段,所以改造会返回合适的POJO?

提问于
用户回答回答于

你会编写一个返回嵌入对象的自定义反序列化器。

假设你的JSON是:

{
    "status":"OK",
    "reason":"some reason",
    "content" : 
    {
        "foo": 123,
        "bar": "some value"
    }
}

你会有一个ContentPOJO:

class Content
{
    public int foo;
    public String bar;
}

然后你写一个反序列化器:

class MyDeserializer implements JsonDeserializer<Content>
{
    @Override
    public Content deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
        throws JsonParseException
    {
        // Get the "content" element from the parsed JSON
        JsonElement content = je.getAsJsonObject().get("content");

        // Deserialize it. You use a new instance of Gson to avoid infinite recursion
        // to this deserializer
        return new Gson().fromJson(content, Content.class);

    }
}

现在,如果你构建一个GsonGsonBuilder和注册解串器:

Gson gson = 
    new GsonBuilder()
        .registerTypeAdapter(Content.class, new MyDeserializer())
        .create();

可以将JSON反序列化到Content

Content c = gson.fromJson(myJson, Content.class);

如果有不同类型的消息,但它们都具有“内容”字段,则可以通过执行以下操作来制作Deserializer:

class MyDeserializer<T> implements JsonDeserializer<T>
{
    @Override
    public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
        throws JsonParseException
    {
        // Get the "content" element from the parsed JSON
        JsonElement content = je.getAsJsonObject().get("content");

        // Deserialize it. You use a new instance of Gson to avoid infinite recursion
        // to this deserializer
        return new Gson().fromJson(content, type);

    }
}

只需为每个类型注册一个实例:

Gson gson = 
    new GsonBuilder()
        .registerTypeAdapter(Content.class, new MyDeserializer<Content>())
        .registerTypeAdapter(DiffContent.class, new MyDeserializer<DiffContent>())
        .create();

当你调用.fromJson()类型被传入解串器时,它应该适用于你所有的类型。

最后,在创建Retrofit实例时:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
用户回答回答于

BrianRoach的解决方案是正确的解决方案。值得注意的是,在嵌套自定义对象都需要自定义的特殊情况下TypeAdapter,您必须注册GSONTypeAdapter新实例,否则第二个TypeAdapter将不会被调用。这是因为我们Gson在自定义解串器中创建了一个新实例。

例如,如果有以下json:

{
    "status": "OK",
    "reason": "some reason",
    "content": {
        "foo": 123,
        "bar": "some value",
        "subcontent": {
            "useless": "field",
            "data": {
                "baz": "values"
            }
        }
    }
}

并且希望将此JSON映射到以下对象:

class MainContent
{
    public int foo;
    public String bar;
    public SubContent subcontent;
}

class SubContent
{
    public String baz;
}

你需要注册SubContentTypeAdapter。为了更强大,可以执行以下操作:

public class MyDeserializer<T> implements JsonDeserializer<T> {
    private final Class mNestedClazz;
    private final Object mNestedDeserializer;

    public MyDeserializer(Class nestedClazz, Object nestedDeserializer) {
        mNestedClazz = nestedClazz;
        mNestedDeserializer = nestedDeserializer;
    }

    @Override
    public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
        // Get the "content" element from the parsed JSON
        JsonElement content = je.getAsJsonObject().get("content");

        // Deserialize it. You use a new instance of Gson to avoid infinite recursion
        // to this deserializer
        GsonBuilder builder = new GsonBuilder();
        if (mNestedClazz != null && mNestedDeserializer != null) {
            builder.registerTypeAdapter(mNestedClazz, mNestedDeserializer);
        }
        return builder.create().fromJson(content, type);

    }
}

然后像这样创建它:

MyDeserializer<Content> myDeserializer = new MyDeserializer<Content>(SubContent.class,
                    new SubContentDeserializer());
Gson gson = new GsonBuilder().registerTypeAdapter(Content.class, myDeserializer).create();

这可以很容易地用于嵌套的“内容”情况,也可以通过简单地传入一个MyDeserializer带有空值的新实例。

扫码关注云+社区