1. 引言
在这个教程中,我们将深入探讨如何在JSONObject
实例中获取值。
有关Java中JSON支持的通用介绍,请参阅JSON-Java的入门指南。
2. JSONObject
结构
JSONObject
是一个类似映射的结构。它以键值对的形式存储数据。虽然键是String
类型,但值可以是多种类型。此外,值类型可能是基本的或复合的。基本类型包括String
、Number
、Boolean
以及JSONObject.NULL
对象。复合类型包括JSONObject
和JSONArray
类型。因此,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数据的嵌套结构。
首先,当键的值是JSONObject
或JSONArray
类型时,我们需要在该值上继续进行递归搜索。其次,当在当前递归调用中找到键时,无论值是基本类型还是复合类型,都需要将其映射的值添加到返回结果中。
以下方法实现了递归搜索:
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上找到。