【问题标题】:GSON - How can I parse two JSONArrays with the same name, but different parameters?GSON - 如何解析两个同名但参数不同的 JSONArray?
【发布时间】:2018-10-05 21:16:41
【问题描述】:

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

“children”通常是一个包含字符串“kind”和Object“data”的Objects数组:

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

我处理得很好。我的问题是,有时,children 将是一个简单的字符串数组:

"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] ]

我该如何处理这些字符串数组的情况?

【问题讨论】:

  • 第二个数据集上面是否还有另一个类?像父母:{孩子:[]}?您还可以将第二个存储到另一个变量中。
  • 所有的孩子都来自一个数据对象,所以数据对象必须能够同时处理这两种类型

标签: java android json gson


【解决方案1】:

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

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。

【讨论】:

    【解决方案2】:

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

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

    public class Parent {
        Collection<?> children;
    }
    

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

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

    【讨论】:

      【解决方案3】:

      抱歉回复晚了,感谢 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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-30
        相关资源
        最近更新 更多