在現代軟件開發中,JSON(JavaScript Object Notation)已成為數據交換的“通用語言”——從前后端接口通信到微服務數據交互,從配置文件解析到日志格式化,幾乎所有場景都離不開JSON的處理。然而,原生JSON框架(如FastJSON、Jackson)的API往往需要大量重復代碼,且空指針、格式異常等問題頻發。
本文分享的JsonUtil
工具類,基于FastJSON、Jackson等主流框架封裝,整合了18+高頻功能,從基礎的格式轉換到復雜的JSON合并,從安全的字段提取到批量內容替換,全方位覆蓋開發需求。下文將從核心能力、源碼解析、實戰場景、優勢對比等維度展開,幫助開發者徹底掌握JSON高效處理技巧。
一、JsonUtil核心能力全景:從基礎到復雜場景
JsonUtil
的設計遵循“業務驅動”原則——所有方法均源自實際開發中的高頻需求。其核心能力可分為六大類,覆蓋JSON處理的全生命周期:
1. JSON合并:多源數據整合的終極方案
在微服務架構中,一個業務數據往往需要從多個接口獲取(如商品詳情需合并基礎信息、庫存、評價等接口數據);在前端開發中,組件數據可能來自多個數據源。JsonUtil
的合并能力正是為解決這類問題設計,支持JSONObject
和JSONArray
兩種合并場景。
- JSONObject合并(
jsonMerge
):以目標JSON為基礎,用源JSON的字段覆蓋或遞歸合并(支持嵌套結構)。 - JSONArray合并(
jsonArrayMerge
):將兩個數組元素合并為新數組,支持嵌套JSON的深拷貝。
2. 格式轉換:對象與JSON的無縫銜接
對象與JSON的轉換是日常開發中最頻繁的操作之一。JsonUtil
封裝了多種轉換邏輯,支持“對象→JSON字符串”“JSON字符串→對象”“集合→JSONArray”等全場景。
- 基礎轉換:
fromJsonStringToT
(JSON字符串→對象)、objectToJson
(對象→JSONObject)。 - 集合轉換:
jsonToList
(JSON→List)、listToJsonArray
(List→JSONArray)。 - Map轉換:
jsonToMap
(JSON→Map)、jsonToList
(JSON→List
3. 安全提取:從JSON中獲取字段的“防坑”實踐
直接從JSON中提取字段時,若字段不存在或類型不匹配,極易拋出NullPointerException
或類型轉換異常。JsonUtil
的提取方法通過“默認值兜底”機制,徹底解決這類問題。
- 基礎類型提取:
getString
(默認空串)、getInt
(默認0)、getBoolean
(默認false)。 - 復雜類型提取:
getJsonObject
(默認null)、getJsonArray
(默認null)。
4. 校驗與修改:JSON格式校驗與內容批量處理
在接收外部數據(如用戶輸入、第三方接口返回)時,需先校驗JSON格式合法性;在日志處理、數據清洗場景中,需批量修改JSON中的特定內容。JsonUtil
提供了完整的校驗與修改能力。
- 格式校驗:
isJsonStr
(FastJSON校驗)、isGsonStr
(Gson校驗)。 - 內容修改:
replaceStringInJson
(遞歸替換JSON中的字符串)。
5. 自定義轉換:按需過濾字段的輕量方案
有時需要將對象轉換為JSON,但僅保留部分字段(如接口返回時隱藏敏感字段)。JsonUtil
的自定義轉換方法通過反射實現字段過濾,無需手動構建Map。
listToStringIncludeArrays
(List對象→僅含指定字段的JSON)。objectToStringIncludeArrays
(單個對象→僅含指定字段的JSON)。
6. 深拷貝:避免引用傳遞的“獨立副本”生成
JSON對象默認是引用傳遞,修改拷貝對象可能影響原對象。JsonUtil
的合并、轉換方法中內置深拷貝邏輯,通過序列化實現對象的完全獨立。
二、核心方法深度解析:從源碼到原理
1. JSON合并(jsonMerge
):遞歸邏輯與邊界處理
業務場景:電商商品詳情頁需要合并“基礎信息接口”(含名稱、價格)和“庫存接口”(含庫存數量、倉庫位置)的返回數據,且需保留雙方的非重復字段。
方法作用:用source
(源JSON)的字段覆蓋target
(目標JSON)的同名字段,若字段是嵌套JSONObject
則遞歸合并,若為JSONArray
則按索引合并元素。
源碼核心邏輯:
public static JSONObject jsonMerge(JSONObject source, JSONObject target) {if (source == null) return target;if (target == null) return source;try {for (String key : source.keySet()) {Object value = source.get(key);// 1. 目標JSON不含該key:直接添加if (!target.containsKey(key)) {target.put(key, value);continue;}// 2. 字段是JSONObject:遞歸合并if (value instanceof JSONObject) {JSONObject mergedValue = jsonMerge((JSONObject) value, target.getJSONObject(key));target.put(key, mergedValue);continue;}// 3. 字段是JSONArray:按索引合并元素(需長度一致)if (value instanceof JSONArray) {JSONArray sourceArray = (JSONArray) value;JSONArray targetArray = target.getJSONArray(key);if (sourceArray.size() != targetArray.size()) {throw new IllegalArgumentException("數組長度不一致:" + key);}for (int i = 0; i < sourceArray.size(); i++) {Object sourceItem = sourceArray.get(i);Object targetItem = targetArray.get(i);// 僅合并數組中的JSONObject元素if (sourceItem instanceof JSONObject && targetItem instanceof JSONObject) {targetArray.set(i, jsonMerge((JSONObject) sourceItem, (JSONObject) targetItem));}}continue;}// 4. 其他類型(字符串、數字等):直接覆蓋target.put(key, value);}} catch (Exception e) {logger.error("JSON合并失敗", e);throw new RuntimeException("合并失敗", e);}return target;
}
關鍵設計:
- 遞歸處理嵌套JSON:若字段是
JSONObject
,通過自身遞歸實現深層合并(如{"a":{"b":1}}
與{"a":{"c":2}}
合并為{"a":{"b":1,"c":2}}
)。 - 數組長度校驗:避免因數組長度不一致導致的索引越界(如合并商品圖片數組時,確保雙方圖片數量相同)。
- 異常兜底:合并失敗時記錄日志并拋出運行時異常,避免靜默失敗(如生產環境中數據合并錯誤需及時報警)。
2. 安全提取(getString
、getInt
):空值處理的最佳實踐
業務痛點:從接口返回的JSON中獲取“用戶手機號”時,若接口未返回該字段,直接調用json.getString("phone")
會返回null
,后續調用phone.length()
將拋出空指針異常。
方法作用:獲取字段值時,若字段不存在或為null
,返回預設默認值(字符串返回空串,整數返回0)。
源碼解析:
public static String getString(JSONObject jsonObject, String key) {// 若jsonObject為null,或字段值為null,返回空串return Objects.nonNull(jsonObject) && Objects.nonNull(jsonObject.getString(key)) ? jsonObject.getString(key) : "";
}public static int getInt(JSONObject jsonObject, String key) {// 若jsonObject為null,或字段值為null,返回0return Objects.nonNull(jsonObject) && Objects.nonNull(jsonObject.getInteger(key))? jsonObject.getInteger(key): 0;
}
使用示例:
JSONObject userJson = JSON.parseObject("{\"name\":\"張三\"}");// 安全獲取存在的字段
String name = JsonUtil.getString(userJson, "name"); // "張三"// 安全獲取不存在的字段(返回空串,避免null)
String phone = JsonUtil.getString(userJson, "phone"); // ""// 安全獲取整數(不存在返回0,避免空指針)
int age = JsonUtil.getInt(userJson, "age"); // 0
3. 自定義轉換(objectToStringIncludeArrays
):反射過濾字段
業務場景:用戶列表接口需要返回用戶信息,但僅保留“id”“name”字段,隱藏“password”“idCard”等敏感信息。
方法作用:通過反射獲取對象的指定字段值,生成僅含這些字段的JSON字符串,無需手動創建Map。
源碼核心邏輯:
// 核心工具方法:將對象轉換為僅含指定字段的Map
private static Map<String, Object> objectToMapIncludeArrays(Object object, String[] arrays) {Map<String, Object> result = new HashMap<>();for (String fieldName : arrays) {if (object instanceof Map) {// 若對象是Map,直接取key對應的值result.put(fieldName, ((Map<?, ?>) object).get(fieldName));} else {// 若對象是普通實體,通過反射獲取字段值try {Object value = getFieldValueByObject(object, fieldName);result.put(fieldName, value);} catch (Exception e) {logger.error("反射獲取字段值失敗", e);}}}return result;
}// 反射獲取字段值(支持父類字段)
private static Object getFieldValueByObject(Object object, String targetFieldName) throws Exception {Class<?> objClass = object.getClass();// 先查當前類字段for (Field field : objClass.getDeclaredFields()) {if (field.getName().equals(targetFieldName)) {field.setAccessible(true); // 突破私有字段訪問限制return field.get(object);}}// 再查父類字段(如User類繼承自BaseEntity,需獲取父類的id字段)Class<?> superClass = objClass.getSuperclass();if