本文针对 Java/Android 原生 org.json.JSONObject 类,详细说明其常用方法、核心区别及避坑要点,适用于日常JSON解析场景,帮助开发者规避常见异常,提升解析安全性和效率。
适用场景:后端接口返回JSON数据解析、本地JSON数据构造与解析;不适用于第三方JSON解析框架(如Gson、FastJson)。
1. 介绍
JSONObject 是原生JSON解析的核心类,用于封装JSON对象(键值对集合),提供了一系列方法用于获取、设置、判断JSON中的键值对,核心分为「严格模式方法」和「容错模式方法」,对应 getXXX 系列和 optXXX 系列。
- 引入依赖
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20200518</version>
</dependency>
2. 常用方法详解
2.1 构造方法
用于创建JSONObject实例,常用2种方式:
java
// 1. 空构造,后续手动添加键值对
JSONObject json = new JSONObject();
// 2. 传入JSON字符串构造(最常用,注意字符串格式必须合法)
String jsonStr = "{\"msgid\":\"123456\",\"name\":\"测试\"}";
JSONObject json = new JSONObject(jsonStr); // 格式错误会抛JSONException
2.2 核心方法:getXXX 系列(严格模式)
核心特点:键(key)不存在、键对应值为null,或值类型不匹配时,直接抛出 JSONException,适用于「必填字段」的解析,强制校验数据完整性。
| 方法 | 功能 | 使用示例 | 异常场景 |
|---|---|---|---|
| getString(String key) | 获取指定key的字符串值 | json.getString("msgid"); | key不存在、值为null、值不是字符串 |
| getInt(String key) | 获取指定key的int值 | json.getInt("age"); | key不存在、值为null、值不是数字(含字符串数字) |
| getLong(String key) | 获取指定key的long值 | json.getLong("id"); | 同getInt,额外注意值超过int范围 |
| getBoolean(String key) | 获取指定key的boolean值 | json.getBoolean("flag"); | key不存在、值为null、值不是boolean(含字符串"true"/"false") |
| getJSONObject(String key) | 获取指定key的JSONObject子对象 | json.getJSONObject("user"); | key不存在、值为null、值不是JSONObject |
| getJSONArray(String key) | 获取指定key的JSONArray数组 | json.getJSONArray("list"); | key不存在、值为null、值不是JSONArray |
2.3 核心方法:optXXX 系列(容错模式)
核心特点:键不存在、值为null时,不抛异常,返回对应类型的默认值;值类型不匹配时,返回默认值(部分场景除外),适用于「非必填字段」的解析,提升程序稳定性。
所有optXXX方法均有重载版本,可自定义默认值(推荐使用,比无参版更安全)。
| 方法 | 无参默认值 | 重载方法(自定义默认值) | 使用示例 | 注意点 |
|---|---|---|---|---|
| optString(String key) | ""(空字符串) | optString(String key, String defaultValue) | optString("name", "未知") | 值为null时返回默认值,无异常 |
| optInt(String key) | 0 | optInt(String key, int defaultValue) | optInt("age", -1) | 值为字符串数字(如"123")时,返回0(坑点) |
| optLong(String key) | 0L | optLong(String key, long defaultValue) | optLong("id", 0L) | 避免用于超长数字解析(如雪花ID),建议先转字符串 |
| optBoolean(String key) | false | optBoolean(String key, boolean defaultValue) | optBoolean("flag", false) | 值为字符串"true"/"false"时,返回false(大坑) |
| optJSONObject(String key) | null | optJSONObject(String key, JSONObject defaultValue) | optJSONObject("user", new JSONObject()) | 返回null时,需手动判空,避免空指针 |
| optJSONArray(String key) | null | optJSONArray(String key, JSONArray defaultValue) | optJSONArray("list", new JSONArray()) | 返回null时,需手动判空,避免空指针 |
2.4 辅助判断方法(高频使用)
用于判断键的存在性和值的有效性,避免盲目调用getXXX/optXXX方法,减少异常。
| 方法 | 功能说明 | 示例(JSON:{"a":null,"b":123}) | 返回结果 |
|---|---|---|---|
| has(String key) | 判断key是否存在(无论值是否为null) | json.has("a")、json.has("c") | true、false |
| isNull(String key) | 判断key不存在,或key存在但值为null | json.isNull("a")、json.isNull("c") | true、true |
| length() | 获取JSON对象中键值对的数量 | json.length() | 2 |
| keys() | 获取所有key的迭代器 | json.keys(); | 迭代器包含"a"、"b" |
2.5 其他常用方法
java
// 1. 向JSON对象添加键值对
json.put("key", "value"); // 支持String、int、boolean等多种类型
// 2. 删除指定key
json.remove("key");
// 3. 将JSON对象转为字符串(格式化/非格式化)
String jsonStr = json.toString(); // 非格式化
String prettyJson = json.toString(4); // 格式化,缩进4个空格
// 4. 获取所有值的集合
JSONArray values = json.toJSONArray(json.keys());
3. 常见坑点及避坑指南(重点)
3.1 数字解析坑(高频踩雷)
后端返回字符串数字(如{"age":"18"})时,getInt/optInt会抛异常或返回0,无法正确解析。
✅ 正确做法:先通过optString获取字符串,再手动转为对应数字类型,避免类型不匹配。
java
// 错误示例
int age = json.optInt("age"); // 后端返回"18",返回0
// 正确示例
String ageStr = json.optString("age", "0");
int age = Integer.parseInt(ageStr);
3.2 布尔值解析坑
后端返回字符串"true"/"false"(如{"flag":"true"})时,optBoolean返回false,getBoolean抛异常。
✅ 正确做法:通过optString获取字符串,再与"true"比较判断。
java
// 错误示例
boolean flag = json.optBoolean("flag"); // 返回false
// 正确示例
boolean flag = "true".equals(json.optString("flag", "false"));
3.3 精度丢失坑
金额、价格等字段,用getDouble/optDouble解析会导致精度丢失(如0.01变成0.0099999999)。
✅ 正确做法:用optString获取字符串,再转为BigDecimal类型。
java
// 错误示例
double price = json.optDouble("price"); // 精度丢失
// 正确示例
String priceStr = json.optString("price", "0.00");
BigDecimal price = new BigDecimal(priceStr);
3.4 对象/数组判空坑
optJSONObject/optJSONArray返回null,而非空对象/空数组,直接调用方法(如length())会抛空指针。
✅ 正确做法:获取后先判空,再进行后续操作。
java
// 正确示例(数组)
JSONArray list = json.optJSONArray("list");
if (list != null && list.length() > 0) {
// 遍历数组
}
// 正确示例(对象)
JSONObject user = json.optJSONObject("user");
if (user != null && !user.isEmpty()) {
// 获取对象中的字段
}
3.5 超大数字解析坑
雪花ID、订单号等超长数字(超过int范围),用getInt解析会抛异常,用optInt返回0。
✅ 正确做法:用optString获取,或用getLong/optLong解析(确保不超过long范围)。
4. 完整示例代码
以下示例涵盖日常解析常见场景,遵循上述规范,可直接参考使用:
java
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.math.BigDecimal;
public class JsonParseDemo {
public static void main(String[] args) {
// 模拟后端返回的JSON字符串
String jsonStr = "{" +
"\"msgid\":\"123456\"," + // 必填字符串
"\"name\":\"张三\"," + // 非必填字符串
"\"age\":\"25\"," + // 非必填数字(字符串格式)
"\"flag\":\"true\"," + // 布尔值(字符串格式)
"\"price\":\"99.99\"," + // 金额
"\"user\":{\"id\":1001,\"phone\":\"13800138000\"}," + // 子对象
"\"list\":[{\"id\":1,\"title\":\"测试1\"},{\"id\":2,\"title\":\"测试2\"}]" + // 数组
"}";
try {
JSONObject json = new JSONObject(jsonStr);
// 1. 必填字段(getString)
String msgid = json.getString("msgid");
// 2. 非必填字符串(optString + 默认值)
String name = json.optString("name", "未知用户");
// 3. 非必填数字(先转字符串,再转int)
String ageStr = json.optString("age", "0");
int age = Integer.parseInt(ageStr);
// 4. 布尔值(字符串判断)
boolean flag = "true".equals(json.optString("flag", "false"));
// 5. 金额(转BigDecimal)
String priceStr = json.optString("price", "0.00");
BigDecimal price = new BigDecimal(priceStr);
// 6. 子对象(optJSONObject + 判空)
JSONObject user = json.optJSONObject("user");
String phone = "";
if (user != null) {
phone = user.optString("phone", "未知号码");
}
// 7. 数组(optJSONArray + 判空 + 遍历)
JSONArray list = json.optJSONArray("list");
if (list != null && list.length() > 0) {
for (int i = 0; i < list.length(); i++) {
JSONObject item = list.getJSONObject(i);
int itemId = item.getInt("id");
String title = item.optString("title", "");
System.out.println("数组项:id=" + itemId + ", title=" + title);
}
}
// 输出解析结果
System.out.println("msgid:" + msgid);
System.out.println("name:" + name);
System.out.println("age:" + age);
System.out.println("flag:" + flag);
System.out.println("price:" + price);
System.out.println("phone:" + phone);
} catch (JSONException e) {
// 捕获解析异常(如必填字段缺失、JSON格式错误)
e.printStackTrace();
System.out.println("JSON解析失败:" + e.getMessage());
} catch (NumberFormatException e) {
// 捕获数字转换异常
e.printStackTrace();
System.out.println("数字转换失败:" + e.getMessage());
}
}
}