獲取 JSONObject 中的值
一、簡介
在本教程中,我們將深入探討在JSONObject
實例中獲取值的細節。
Java中對JSON支持的一般介紹,請查看JSON-Java介紹。
2. JSONObject
結構
JSONObject
是一種類似地圖的結構。它將其數據保存為一組鍵值對。雖然鍵是String
類型,但值可能是多種類型。此外,值類型可以是 primitive 或 compound 。基元是String
、 Number, and
Boolean types,
或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
實例,而特定方法返回一個已經轉換的實例。讓我們使用通用的get()
方法獲取 JSON 數據的“family”字段。我們假設JSON數據已經初步加載到jsonObject
變量中,該變量是JSONObject
類型:
JSONArray family = (JSONArray) jsonObject.get("family");
我們可以使用JSONArray
的特定 getter 以更具可讀性的方式執行相同的操作:
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.
此外,當 JSON 數據的結構是動態的時,我們需要在代碼中添加徹底的檢查。例如,對於所有get()
方法,我們需要將代碼包含在try-catch
塊中。此外,對於opt()
方法,我們需要添加空值或特定值檢查。
5. 遞歸獲取值
相比之下,在 JSON 數據中獲取值的遞歸方法靈活且不易出錯。在實現這種方法時,我們需要考慮 JSON 數據的嵌套結構。
首先,當key的值是JSONObject
或JSONArray
類型時,我們需要在該值中向下傳播遞歸搜索。其次,當在當前遞歸調用中找到key時,我們需要將其映射值添加到返回結果中,無論該值是否為原始類型。
下面的方法實現了遞歸搜索:
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");
}
六,結論
在本文中,我們討論了獲取JSONObject.
可以在 GitHub 上找到本次討論中使用的片段的完整代碼。