检查嵌套JSON中是否存在某个键

7

我遇到了一个问题,需要检查嵌套JSON对象中是否存在某个键。我的嵌套JSON对象是指:父JSON对象中的一个键的值是一个JSON对象。因此,我需要检查这个键在整个JSON对象中是否存在。我得到的数据是一个String对象。我知道可以将这个String对象解析为JSON对象。

{
"claim_loss_type_cd": "TEL",
"claim_type": "002",
"claim_reason": "001",
"policy_number": "1234kk3366ff664",
"info": {
    "ApplicationContext": {
        "country": "US"
    }
  }
}

我曾使用containsKey()方法来检查主JSON对象中的键是否存在,这个方法是有效的。但是对于检查像"info"这样的内部JSON对象,我需要再将该Object解析为JSON对象,然后再次检查该键是否存在。

        String jsonString = "My JSON String here";
        JSONObject finalResponse = new JSONObject(jsonString);
        finalResponse.containsKey("country"); // will return false
        JSONObject intermediateResponse = (JSONObject)finalResponse.get("info");
        intermediateResponse.containsKey("country"); // will return true

有没有更好的方法、API或者方法可以在不需要解析内部JSON对象的情况下检查内部JSON对象中是否存在,我正在使用Websphere应用服务器的原生IBM库com.ibm.json.java.JSONObject.JSONObject(),不使用额外的JSON解析器。
考虑上述JSON,例如"claim_type"是父JSON对象中的键,但"info"本身是一个JSON对象。所以我需要做的是检查完整的JSON中是否存在一个键,无论是在父级还是在任何子级JSON对象中,例如这里的"country"键。
编辑:感谢@chsdk,我找到了一种解决方案。如果有人使用其他API找到了任何解决方案,请回复,因为下面的解决方案将递归考虑进去,并且可能具有较大的空间/时间复杂度。
public static boolean checkKey(JSONObject object, String searchedKey) {
    boolean exists = object.containsKey(searchedKey);
    if(!exists) {      
         Set<String> keys = object.keySet();
         for(String key : keys){
             if ( object.get(key) instanceof JSONObject ) {
                    exists = checkKey((JSONObject)object.get(key), searchedKey);
            }
         }
    }
    return exists;
}

你使用了任何 JSON 解析器,例如 Jakson 等吗? - Muhammad Suleman
1
各位,JSON(这个词)不是代码,请不要将其格式化为代码块。 - Tim
@TimCastelijns 有人建议我将JSON转换为代码,所以我接受了这个编辑建议。 - AnkeyNigam
2
如果您有JSON作为原始字符串,它是否可以与正则表达式\"country\"\s*:进行匹配检查? - Adrian Shum
@MuhammadSuleman 不,我没有使用任何额外的解析器。 - AnkeyNigam
4个回答

8
您可以使用JSONObject来解析您的JSON,并使用其has(String key)方法检查此Json中是否存在键:
 String str="{\"claim_loss_type_cd\": \"TEL\",\"claim_type\":\"002\",\"claim_reason\": \"001\",\"policy_number\":\"1234kk3366ff664\",\"info\": {\"ApplicationContext\":{\"country\": \"US\"}}}";
 Object obj=JSONValue.parse(str);
 JSONObject json = (JSONObject) obj;
 //Then use has method to check if this key exists or not
 System.out.println(json.has("claim_type")); //Returns true

编辑:

或者更好的方法是,您可以使用indexOf()方法检查JSON字符串是否包含此键值:

String str="{\"claim_loss_type_cd\": \"TEL\",\"claim_type\":\"002\",\"claim_reason\": \"001\",\"policy_number\":\"1234kk3366ff664\",\"info\": {\"ApplicationContext\":{\"country\": \"US\"}}}";
System.out.println(str.indexOf("claim_type")>-1); //Returns true

编辑2:

看一下这个方法,它遍历嵌套的对象来检查是否存在该键。

public boolean keyExists(JSONObject  object, String searchedKey) {
    boolean exists = object.has(searchedKey);
    if(!exists) {      
        Iterator<?> keys = object.keys();
        while( keys.hasNext() ) {
            String key = (String)keys.next();
            if ( object.get(key) instanceof JSONObject ) {
                    exists = keyExists(object.get(key), searchedKey);
            }
        }
    }
    return exists;
}

Object obj=JSONValue.parse(str);
JSONObject json = (JSONObject) obj;
System.out.println(keyExists(json, "country")); //Returns true

我已经编辑了我的问题以便更好地解释。我不想解析内部的JSON对象。这个 has() 方法能够检查整个 JSON 字符串,并检查一个键是否存在吗? - AnkeyNigam
是的,看起来很有前途,可能可以解决需求,但是性能问题怎么样?调用keyExists()方法进行递归会不会影响性能? - AnkeyNigam
可以放心地进行递归调用,因为如果键存在,则不会从头开始进入循环,并且仅在嵌套的JSONObject中才会进行递归调用。 - cнŝdk
谢谢 chsdk,它工作得很好。我根据 IBM 的 JSONObject 类和方法进行了更改。 - AnkeyNigam
太好了,很高兴能帮到你。 - cнŝdk
显示剩余3条评论

0
两种建议递归JsonObject的解决方案都有一个小bug:它们找到目标键之后没有停止迭代。因此,你需要在while循环中加入break语句,否则循环会继续下去,如果存在下一个键,它将检查该键等等。而为“country”键进行搜索的代码示例之所以有效,仅仅是因为'country'恰巧是其JsonObject中的最后一个键。
示例:
 /* ... */
    while (keys.hasNext()) {
          nextKey = (String) keys.next();

          try {
            if (json.get(nextKey) instanceof JSONObject) {
              exists = hasKey(json.getJSONObject(nextKey), key);

              if(exists){
                  break;
              }

            }
          } catch (JSONException e) {
            e.printStackTrace();
          }
        }
    /* ... */

0

我遇到了类似的问题,为了解决它,我将JSON转换为Map,并收集键值对,然后迭代它们以获取我所需的值(这有点像反复调用同一个方法,但比递归编程更好,希望能有所帮助)。

public static void jsonToMap(String t) throws JSONException {

    Map<String,String> map = new HashMap<>();
    getJsonObjectMap(t).entrySet()
        .stream()
        .filter(s->s.getKey().equals("info"))
        .forEach(u -> {
            try {
                getJsonObjectMap(u.getValue()).forEach(map::put);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        });
        
    map.entrySet().forEach(System.out::println);

}

private static Map<String,String> getJsonObjectMap(String t) throws JSONException {

    HashMap<String,String>  map = new HashMap<>();
    JSONObject jObject = new JSONObject(t);
    Iterator<?> keys = jObject.keys();
    while( keys.hasNext() ){
        String key = (String)keys.next();
        String value = jObject.getString(key);
        map.put(key, value);
    }
    return map;
}

0
一个准备就绪的方法,正确地转换类型:
/**
 * JSONObject contains the given key. Search is also done in nested 
 * objects recursively.
 *
 * @param json JSONObject to serach in.
 * @param key Key name to search for.
 * @return Key is found.
 */
public static boolean hasKey(
  JSONObject json,
  String key) {

  boolean exists = json.has(key);
  Iterator<?> keys;
  String nextKey;

  if (!exists) {

    keys = json.keys();

    while (keys.hasNext()) {
      nextKey = (String) keys.next();

      try {
        if (json.get(nextKey) instanceof JSONObject) {
          exists =
            hasKey(
              json.getJSONObject(nextKey),
              key);
        }
      } catch (JSONException e) {
        e.printStackTrace();
      }
    }
  }

  return exists;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接