GSON-如何解析两个具有相同名称但参数不同的JSONArray?

时间:2018-10-05 21:16:42

标签: java android json gson

在Reddit JSON API中,注释可以包含两种不同类型的JSONArray,都称为“子级”。

“子级”通常是包含字符串“ kind”和对象“ data”的Object数组:

"children": [ { "kind": "t3", "data": {} } ...]

我一直在处理这些罚款。我的问题是,有时,子级将是一个简单的String数组:

"children": [ "e78i3mq", "e78hees", "e78jq6q" ]

解析这些内容时,GSON会引发如下异常:

  

由以下原因引起:java.lang.IllegalStateException:预期为BEGIN_OBJECT,但   在第1行第3780列的路径处为STRING   $ [1] .data.children [0] .data.replies.data.children [0] .data.replies.data.children [0] .data.replies.data.children [0] .data.children [0 ]

如何处理这些String数组案例?

3 个答案:

答案 0 :(得分:2)

如果在某些情况下相同的端点返回不同的类型,我建议将该部分包装在对象中,并使用反序列化器检查类型并相应地分配。您可以执行以下操作:

public Parent serialize(String jsonString) {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(WrappedChild.class, new ChildDeserializer());
    Gson gson = builder.create();
    return gson.fromJson(jsonString, Parent.class);
}

class Parent {
    public List<WrappedChild> children;
}

class ObjectChild {
    public String body;
}

class WrappedChild {
    public ObjectChild objectChild;
    public String stringChild;
}

class ChildDeserializer implements JsonDeserializer<WrappedChild> {
    private Gson gson = new Gson();

    @Override
    public WrappedChild deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json != null) {
            if (json.isJsonObject()) {
                WrappedChild result = new WrappedChild();
                result.objectChild = gson.fromJson(json, ObjectChild.class);
                return result;
            } else if (json.isJsonPrimitive()) {
                WrappedChild result = new WrappedChild();
                result.stringChild = json.getAsString();
                return result;
            }
        }
        return null; // Or throw new Exception("Unknown child type");
    }
}

如果您要使用改造,只需在创建服务时将构建器创建的Gson作为参数传递给GsonConverterFactory.create

答案 1 :(得分:0)

您应该仔细研究answer from Emre Eran,因为那样您将完全控制反序列化。我将给出另一种方法,在某些情况下可能需要较少的精力。它基于Gson反序列化的“基本情报”。

如果您声明包含children的类,例如:

public class Parent {
    Collection<?> children;
}

Gson尽力“猜测”对象类型。如果它面对一个简单的字符串,它将被反序列化为String。如果它像第一个Json示例中那样面对数据,它将反序列化为com.google.gson.internal.LinkedTreeMap树的Java版本的Json

因此,根据第一个示例中data对象的复杂程度以及整体使用结果的方式,您可能不需要编写自定义反序列化器(无论如何最终还是更好的解决方案)。

答案 2 :(得分:0)

对不起,您回答的太晚了,谢谢您引导我朝着正确的方向Emre!

我最终使GsonBuilder与自定义方法getGsonAdaptedData一起使用。

在后台线程中获取JSON响应后:

...
Gson gson = new GsonBuilder().registerTypeAdapter(Data.class, (JsonDeserializer<Data>) (arg0, arg1, arg2) -> {
        JsonObject dataJsonObject = arg0.getAsJsonObject();
        Data data = new Gson().fromJson(dataJsonObject, Data.class);
        return RedditUtils.getGsonAdaptedData(dataJsonObject.get("children").getAsJsonArray(), data);
    }).create();
    final Feed responseSubredditFeed = gson.fromJson(jsonString, Feed.class);
...

RedditUtils.getGsonAdaptedData

// JSON returned for Reddit comments can contain two types of arrays named "children"
// This method checks to see if we were given a Children array or String array
// JSON member "replies" is similar, and can be found in the Data of some Children
// If the method finds a nested "children" array, it recursively adapts its Data
public static Data getGsonAdaptedData(JsonArray childrenJsonArray, Data data) {

    if (childrenJsonArray.size() > 0) {

        Gson gson = new Gson();

        if (childrenJsonArray.get(0).isJsonObject()) {

            data.setChildrenList(gson.fromJson(childrenJsonArray,
                    new TypeToken<List<Children>>() {
                    }.getType()));

            // Loops through every Data object in the array looking for children and replies
            for (int i = 0; i < childrenJsonArray.size(); i++) {

                JsonObject nestedDataJsonObject = childrenJsonArray.get(i).getAsJsonObject().get("data").getAsJsonObject();

                if (nestedDataJsonObject.has("children")) {
                    getGsonAdaptedData(nestedDataJsonObject.get("children").getAsJsonArray(), data.getChildren().get(i).getData());

                } else if (nestedDataJsonObject.has("replies") && nestedDataJsonObject.get("replies").isJsonObject()) {

                    data.getChildren().get(i).getData().setRepliesObject(gson.fromJson(nestedDataJsonObject.get("replies"),
                            new TypeToken<Replies>() {
                            }.getType()));

                    getGsonAdaptedData(nestedDataJsonObject.get("replies").getAsJsonObject().get("data").getAsJsonObject().get("children").getAsJsonArray(), data.getChildren().get(i).getData());
                }
            }
        } else {

            data.setRepliesList(gson.fromJson(childrenJsonArray,
                    new TypeToken<List<String>>() {
                    }.getType()));
        }
    }

    return data;
}