1. 引言

在这个教程中,我们将深入探讨如何在JSONObject实例中获取值。

有关Java中JSON支持的通用介绍,请参阅JSON-Java的入门指南

2. JSONObject结构

JSONObject是一个类似映射的结构。它以键值对的形式存储数据。虽然键是String类型,但值可以是多种类型。此外,值类型可能是基本的或复合的。基本类型包括StringNumberBoolean以及JSONObject.NULL对象。复合类型包括JSONObjectJSONArray类型。因此,JSON数据可以具有任意复杂性和嵌套层次。

因此,在嵌套结构中获取值需要更多的努力。接下来,让我们参考一个虚构员工的JSON数据示例:

{
  "name" : "Bob",
  "profession" : "Software engineer",
  "department" : "Research",
  "age" : 40,
  "family" : [
    {
      "wife" : {
        "name" : "Alice",
        "profession" : "Doctor",
        "age" : 38
      }
    },
    {
      "son" : {
        "name" : "Peter",
        "occupation" : "Schoolboy",
        "age" : 11
      }
    }
  ],
  "performance" : [
    {
      "2020" : 4.5
    },
    {
      "2021" : 4.8
    }
  ]
}

3. JSONObject的getter方法

首先,我们来看看JSONObject类提供了哪些getter API。主要有两种类型的函数——get()opt()方法。两组方法之间的区别在于,当找不到键时,get()方法会抛出异常,而opt()方法不会抛出异常,而是返回null或特定值,具体取决于方法。

此外,每组都有一个泛型方法和一些带有类型转换的具体方法。泛型方法返回Object实例,而具体方法则返回已经转换的实例。假设我们已经将JSON数据预先加载到jsonObject变量中(类型为JSONObject),如下所示,我们将使用泛型的get()方法获取“family”字段:

    JSONArray family = (JSONArray) jsonObject.get("family");

我们可以使用更易读的特定getter方法来获取JSONArray的值:

    JSONArray family = jsonObject.getJSONArray("family");

4. 直接获取值

直接获取值的方法是通过沿着到达所需值的路径逐级获取中间值。下面的代码展示了如何直接获取员工儿子的名字:

    JSONArray family = jsonObject.getJSONArray("family");
    JSONObject sonObject = family.getJSONObject(1);
    JSONObject sonData = sonObject.getJSONObject("son");
    String sonName = sonData.getString("name");
    Assertions.assertEquals(sonName, "Peter");

可以看到,直接获取值的方法存在局限性。首先,我们需要知道JSON数据的确切结构。其次,我们需要了解到达每个值的路上的数据类型,以便使用正确的JSONObject getter方法。

此外,当JSON数据结构动态时,我们需要在代码中添加详细的检查。例如,对于所有get()方法,我们需要在代码中包含try-catch块。对于opt()方法,我们需要添加null或特定值检查。

5. 递归获取值

相比之下,递归获取JSON数据中的值的方法更灵活,出错的可能性也较小。实现此方法时,我们需要考虑JSON数据的嵌套结构。

首先,当键的值是JSONObjectJSONArray类型时,我们需要在该值上继续进行递归搜索。其次,当在当前递归调用中找到键时,无论值是基本类型还是复合类型,都需要将其映射的值添加到返回结果中。

以下方法实现了递归搜索:

    public List<String> getValuesInObject(JSONObject jsonObject, String key) {
        List<String> accumulatedValues = new ArrayList<>();
        for (String currentKey : jsonObject.keySet()) {
            Object value = jsonObject.get(currentKey);
            if (currentKey.equals(key)) {
                accumulatedValues.add(value.toString());
            }

            if (value instanceof JSONObject) {
                accumulatedValues.addAll(getValuesInObject((JSONObject) value, key));
            } else if (value instanceof JSONArray) {
                accumulatedValues.addAll(getValuesInArray((JSONArray) value, key));
            }
        }

        return accumulatedValues;
    }

    public List<String> getValuesInArray(JSONArray jsonArray, String key) {
        List<String> accumulatedValues = new ArrayList<>();
        for (Object obj : jsonArray) {
            if (obj instanceof JSONArray) {
                accumulatedValues.addAll(getValuesInArray((JSONArray) obj, key));
            } else if (obj instanceof JSONObject) {
                accumulatedValues.addAll(getValuesInObject((JSONObject) obj, key));
            }
        }

        return accumulatedValues;
    }

为了简化,我们提供了两个单独的方法:一个用于在JSONObject实例中递归搜索,另一个用于JSONArray实例。JSONObject是映射结构,而JSONArray是数组结构,因此它们的迭代方式不同。将所有逻辑放在单个方法中会使代码变得复杂,因为需要处理类型转换和if-else分支。

最后,让我们编写测试代码,用于getValuesInObject()方法:

    @Test
    public void getAllAssociatedValuesRecursively() {
        List<String> values = jsonObjectValueGetter.getValuesInObject(jsonObject, "son");
        Assertions.assertEquals(values.size(), 1);

        String sonString = values.get(0);
        Assertions.assertTrue(sonString.contains("Peter"));
        Assertions.assertTrue(sonString.contains("Schoolboy"));
        Assertions.assertTrue(sonString.contains("11"));

        values = jsonObjectValueGetter.getValuesInObject(jsonObject, "name");
        Assertions.assertEquals(values.size(), 3);

        Assertions.assertEquals(values.get(0), "Bob");
        Assertions.assertEquals(values.get(1), "Alice");
        Assertions.assertEquals(values.get(2), "Peter");
    }

6. 总结

在这篇文章中,我们讨论了如何在JSONObject中获取值。本文中使用的片段的完整代码可以在GitHub上找到。