【问题标题】:Ignore duplicate keys in JSONObject忽略 JSONObject 中的重复键
【发布时间】:2018-06-01 06:14:41
【问题描述】:

我有一个 JSON 格式的字符串

String str = "{\"innerkey1\":\"innervalue1\",
\"innerkey2\":\"innervalue2\",\"innerkey1\":\"innervalue3\"}";

当我试图从这个字符串创建一个对象时

try  {
    JSONObject obj = new JSONObject(str); 
} catch(Exception e) {
     e.printStackTrace();
}

我遇到了一个错误

org.json.JSONException: Duplicate key "innerkey1"

有什么方法可以通过忽略字符串中的最后一个键来创建 JSONObject?

【问题讨论】:

  • 那个 JSON 看起来很奇怪。 key1:value1 可以,但是你有一个数组作为值;钥匙在哪里?
  • @BuzzMoschetti 现在,您可以查找有效的 json 字符串。

标签: java json


【解决方案1】:

你可以使用objectMapper来跳过这个错误

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"test\", \"name\":\"new test\"}";

Map<String, Object> map = new HashMap<String, Object>();

map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});

现在您可以轻松地从地图转换为 JSONObject

new JSONObject(map);

【讨论】:

    【解决方案2】:

    我有一个JSON parser,它允许使用代码将重复键视为数组中的元素。请参阅“文档生成器代码”部分。

    【讨论】:

      【解决方案3】:

      这不是最简单的解决方案,但它允许您在输入字符串中有重复项并选择如何处理它们(重命名、删除等)。您应该使用Gson 库并注册自己的TypeAdapter 负责对象的序列化/反序列化。它看起来像这样:

      class NoDuplicatesAdapter extends TypeAdapter<HashMap<String, String>> {
          @Override
          public void write(JsonWriter out, HashMap<String, String> value) throws IOException {
              out.beginObject();
              for (Map.Entry<String, String> e: value.entrySet()) {
                  out.name(e.getKey()).value(e.getValue());
              }
              out.endObject();
          }
          @Override
          public HashMap<String, String> read(JsonReader in) throws IOException {
              final HashMap<String, String> map = new HashMap<>();
              in.beginObject();
              while (in.hasNext()) {
                  String name = in.nextName();
                  // putting value to the map only if this key is not present;
                  // here you can actually find duplicate keys and decide what to do with them
                  map.putIfAbsent(name, in.nextString());
              }
              in.endObject();
              return map;
          }
      }
      

      这是最复杂的部分,现在我们只需要读取JSON 字符串:

      String str = "{\"innerkey1\":\"innervalue1\", \"innerkey2\":\"innervalue2\",\"innerkey1\":\"innervalue3\"}";
      
      Type mapType = new TypeToken<Map<String, String>>() {}.getType();
      
      Map<String, String> map = new GsonBuilder()
              .registerTypeAdapter(mapType, new NoDuplicatesAdapter())
              .create()
              .fromJson(str, mapType);
      

      map 将仅包含第一个 "innerkey1"

      【讨论】:

        【解决方案4】:

        这是 JSON 规范的问题。根据使用的规范,可以解释是否完全允许重复键。在this thread 中,已被广泛认同的定义是JSONObject 中的key 必须是唯一的。

        因此,您不应尝试解析可能无效的 JSON 对象,而应使用有效的对象。

        但我对解决方案有一个小建议:无论您将其付诸实践,您都可以使用(多次出现的)键并将存储在那里的所有对象作为数组分配给这个(现在是唯一的)键。因此,您合并了几个相同的键,并将链接的对象合并到一个数组中。这至少会使 JSON 对象有效:

        {
            "a" : "x",
            "a" : "y"
        }
        // becomes
        {
            "a" : ["x", "y"]
        }
        

        【讨论】:

        • 不幸的是,我无法更改我的 Json 字符串,因为我是从不同的 api 获取的。
        【解决方案5】:

        很遗憾,没有。虽然它在技术上并不违反 JSON 标准,但它不是可以轻松使用的东西/您将很难找到可以解析它的库。

        请看这里:https://stackoverflow.com/a/21833017/8972283

        如果可能,我建议您更新 JSON 的创建方式。

        【讨论】:

          猜你喜欢
          • 2015-10-15
          • 2011-01-22
          • 1970-01-01
          • 2015-05-25
          • 1970-01-01
          • 2012-08-04
          • 1970-01-01
          • 2016-04-16
          • 1970-01-01
          相关资源
          最近更新 更多