在现代软件开发中,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