JSON路径工具类JsonPathUtil
的实现与应用
作者:zibo
日期:2024/11/25
口号:慢慢学,不要停。
文章目录
- JSON路径工具类`JsonPathUtil`的实现与应用
- 〇、完整代码
- 一、引言
- 二、功能概述
- 三、代码实现详解
- 1. 工具类基础结构
- 2. 核心方法`getValue`
- 3. 处理表达式片段`processPart`
- 4. 处理数组类型的表达式片段`processArrayPart`
- 5. 获取对象的字段值`getFieldValue`
- 6. 测试主方法`main`
- 四、应用示例
- 五、总结
- 六、后记
〇、完整代码
package com.kumy.requrchase.treasure.service.letusign.impl;import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;/*** JSON路径工具类* 用于根据表达式获取JSON字符串中的值* 支持以下功能:* 1. 获取普通属性值,如: user.name* 2. 获取数组元素,如: users[0].name* 3. 支持多层嵌套,如: company.department.employees[0].name* * @author zibo* @date 2024/11/25* @slogan 慢慢学,不要停。*/
public class JsonPathUtil {// 定义常量,提高代码可维护性private static final String DOT_SEPARATOR = "\\.";private static final String LEFT_BRACKET = "[";private static final String RIGHT_BRACKET = "]";private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();private JsonPathUtil() {throw new IllegalStateException("工具类不允许实例化");}/*** 根据表达式获取对象的值** @param jsonString JSON字符串,不能为空* @param expression 表达式,不能为空* @return 表达式对应的值* @throws IllegalArgumentException 参数校验异常* @throws Exception 解析异常*/public static Object getValue(String jsonString, String expression) throws Exception {// 参数校验if (Objects.isNull(jsonString) || Objects.isNull(expression)) {throw new IllegalArgumentException("参数不能为空");}// 将 JSON 字符串转换为 Map 对象Map<String, Object> rootObject = OBJECT_MAPPER.readValue(jsonString, new TypeReference<Map<String, Object>>() {});// 分割表达式并处理String[] parts = expression.split(DOT_SEPARATOR);Object currentObject = rootObject;for (String part : parts) {currentObject = processPart(currentObject, part);if (Objects.isNull(currentObject)) {return null;}}return currentObject;}/*** 处理表达式片段* * @param currentObject 当前对象* @param part 表达式片段* @return 处理后的对象* @throws Exception 处理异常*/private static Object processPart(Object currentObject, String part) throws Exception {if (part.contains(LEFT_BRACKET)) {return processArrayPart(currentObject, part);}return getFieldValue(currentObject, part);}/*** 处理数组类型的表达式片段* * @param currentObject 当前对象* @param part 表达式片段* @return 处理后的对象* @throws Exception 处理异常*/private static Object processArrayPart(Object currentObject, String part) throws Exception {String fieldName = part.substring(0, part.indexOf(LEFT_BRACKET));int index = Integer.parseInt(part.substring(part.indexOf(LEFT_BRACKET) + 1, part.indexOf(RIGHT_BRACKET)));Object arrayObject = getFieldValue(currentObject, fieldName);return arrayObject instanceof List ? ((List<?>) arrayObject).get(index) : null;}/*** 获取对象的字段值** @param object 对象,不能为空* @param fieldName 字段名,不能为空* @return 字段值* @throws Exception 反射异常*/@SuppressWarnings("all")private static Object getFieldValue(Object object, String fieldName) throws Exception {if (Objects.isNull(object) || Objects.isNull(fieldName)) {return null;}if (object instanceof Map) {return ((Map<?, ?>) object).get(fieldName);}Field field = object.getClass().getDeclaredField(fieldName);field.setAccessible(true);return field.get(object);}public static void main(String[] args) {try {// 测试JSON字符串String jsonString = "{"+ "\"userInfo\": {"+ "\"id\": 1,"+ "\"photoPath\": \"yx.mm.com\","+ "\"realName\": \"张三\","+ "\"examInfoDict\": ["+ "{\"id\": 1, \"examType\": 0, \"answerIs\": 1},"+ "{\"id\": 2, \"examType\": 0, \"answerIs\": 0}"+ "]"+ "},"+ "\"flag\": 1"+ "}";// 测试不同场景System.out.println("测试普通属性: " + getValue(jsonString, "userInfo.realName")); // 输出张三System.out.println("测试数组访问: " + getValue(jsonString, "userInfo.examInfoDict[0].id")); // 输出1System.out.println("测试空值处理: " + getValue(jsonString, "userInfo.notExist")); // 输出null} catch (Exception e) {System.err.println("处理异常: " + e.getMessage());e.printStackTrace();}}
}
一、引言
在日常的Java开发中,经常需要根据特定的路径或表达式,从JSON字符串中提取所需的数据。虽然市场上有诸如JsonPath等强大的工具可以实现这一需求,但有时候我们需要一个轻量级、可自定义的解决方案。本文将介绍一个自定义实现的JSON路径工具类JsonPathUtil
,它可以根据表达式从JSON字符串中获取对应的值,支持获取普通属性、数组元素以及多层嵌套的属性值。
二、功能概述
JsonPathUtil
工具类的主要功能包括:
- 获取普通属性值:如
user.name
,获取user
对象的name
属性值。 - 获取数组元素:如
users[0].name
,获取users
数组中第一个元素的name
属性值。 - 支持多层嵌套:如
company.department.employees[0].name
,获取嵌套结构中指定员工的姓名。
三、代码实现详解
1. 工具类基础结构
首先,定义了JsonPathUtil
工具类,并声明了一些常量:
package com.kumy.requrchase.treasure.service.letusign.impl;import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;public class JsonPathUtil {private static final String DOT_SEPARATOR = "\\.";private static final String LEFT_BRACKET = "[";private static final String RIGHT_BRACKET = "]";private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();private JsonPathUtil() {throw new IllegalStateException("工具类不允许实例化");}// 其他方法...
}
解析:
- 使用了
ObjectMapper
来处理JSON字符串的解析。 - 工具类的构造方法被私有化,防止实例化。
2. 核心方法getValue
getValue
方法是工具类的核心,用于根据表达式从JSON字符串中获取对应的值。
public static Object getValue(String jsonString, String expression) throws Exception {// 参数校验if (Objects.isNull(jsonString) || Objects.isNull(expression)) {throw new IllegalArgumentException("参数不能为空");}// 将 JSON 字符串转换为 Map 对象Map<String, Object> rootObject = OBJECT_MAPPER.readValue(jsonString, new TypeReference<Map<String, Object>>() {});// 分割表达式并处理String[] parts = expression.split(DOT_SEPARATOR);Object currentObject = rootObject;for (String part : parts) {currentObject = processPart(currentObject, part);if (Objects.isNull(currentObject)) {return null;}}return currentObject;
}
解析:
- 参数校验:确保
jsonString
和expression
不为空,否则抛出IllegalArgumentException
。 - JSON解析:使用
ObjectMapper
将JSON字符串解析为Map<String, Object>
类型的rootObject
。 - 表达式解析:根据
.
分隔符,将表达式拆分为多个部分parts
,然后逐一处理每个部分。 - 逐层解析:通过循环,每次处理表达式的一部分,并不断更新
currentObject
,直到获取最终的值。
3. 处理表达式片段processPart
该方法用于处理表达式中的每一部分,判断是普通属性还是数组访问。
private static Object processPart(Object currentObject, String part) throws Exception {if (part.contains(LEFT_BRACKET)) {return processArrayPart(currentObject, part);}return getFieldValue(currentObject, part);
}
解析:
- 如果表达式部分包含
[
,说明需要处理数组,调用processArrayPart
方法。 - 否则,直接调用
getFieldValue
获取属性值。
4. 处理数组类型的表达式片段processArrayPart
该方法用于解析数组元素的访问。
private static Object processArrayPart(Object currentObject, String part) throws Exception {String fieldName = part.substring(0, part.indexOf(LEFT_BRACKET));int index = Integer.parseInt(part.substring(part.indexOf(LEFT_BRACKET) + 1, part.indexOf(RIGHT_BRACKET)));Object arrayObject = getFieldValue(currentObject, fieldName);return arrayObject instanceof List ? ((List<?>) arrayObject).get(index) : null;
}
解析:
- 获取字段名和索引:通过字符串操作,提取数组字段名
fieldName
和索引index
。 - 获取数组对象:使用
getFieldValue
方法获取对应的数组对象arrayObject
。 - 获取数组元素:检查
arrayObject
是否为List
的实例,如果是,则返回对应索引的元素。
5. 获取对象的字段值getFieldValue
该方法用于获取当前对象中指定字段的值。
@SuppressWarnings("all")
private static Object getFieldValue(Object object, String fieldName) throws Exception {if (Objects.isNull(object) || Objects.isNull(fieldName)) {return null;}if (object instanceof Map) {return ((Map<?, ?>) object).get(fieldName);}Field field = object.getClass().getDeclaredField(fieldName);field.setAccessible(true);return field.get(object);
}
解析:
- 空值检查:如果
object
或fieldName
为null
,直接返回null
。 - 处理Map类型:如果当前对象是
Map
,直接获取对应键的值。 - 处理普通对象:使用反射获取对象的字段值,即使字段是私有的(通过
setAccessible(true)
)。
6. 测试主方法main
编写了一个main
方法用于测试工具类的功能。
public static void main(String[] args) {try {// 测试JSON字符串String jsonString = "{"+ "\"userInfo\": {"+ "\"id\": 1,"+ "\"photoPath\": \"yx.mm.com\","+ "\"realName\": \"张三\","+ "\"examInfoDict\": ["+ "{\"id\": 1, \"examType\": 0, \"answerIs\": 1},"+ "{\"id\": 2, \"examType\": 0, \"answerIs\": 0}"+ "]"+ "},"+ "\"flag\": 1"+ "}";// 测试不同场景System.out.println("测试普通属性: " + getValue(jsonString, "userInfo.realName")); // 输出张三System.out.println("测试数组访问: " + getValue(jsonString, "userInfo.examInfoDict[0].id")); // 输出1System.out.println("测试空值处理: " + getValue(jsonString, "userInfo.notExist")); // 输出null} catch (Exception e) {System.err.println("处理异常: " + e.getMessage());e.printStackTrace();}
}
解析:
- 构造了一个包含嵌套对象和数组的JSON字符串。
- 测试了获取普通属性、数组元素以及处理不存在的属性的情况。
- 输出结果验证了工具类的功能。
四、应用示例
为了更清晰地展示JsonPathUtil
的应用,下面提供一个实际例子。
示例JSON字符串:
{"employee": {"name": "李华","age": 30,"department": {"name": "研发部","location": "北京"},"skills": [{"name": "Java", "level": "高级"},{"name": "Python", "level": "中级"}]}
}
示例代码:
String jsonString = "{...}"; // 如上所示的JSON字符串// 获取员工姓名
String name = (String) JsonPathUtil.getValue(jsonString, "employee.name");
System.out.println("员工姓名:" + name); // 输出:员工姓名:李华// 获取部门名称
String departmentName = (String) JsonPathUtil.getValue(jsonString, "employee.department.name");
System.out.println("部门名称:" + departmentName); // 输出:部门名称:研发部// 获取第一项技能的名称
String firstSkill = (String) JsonPathUtil.getValue(jsonString, "employee.skills[0].name");
System.out.println("第一项技能:" + firstSkill); // 输出:第一项技能:Java// 尝试获取不存在的属性
Object nonExistent = JsonPathUtil.getValue(jsonString, "employee.address");
System.out.println("不存在的属性:" + nonExistent); // 输出:不存在的属性:null
解析:
- 使用
JsonPathUtil.getValue
方法,根据不同的表达式,成功获取了嵌套对象和数组中的值。 - 当尝试获取不存在的属性时,方法返回
null
,程序没有抛出异常,这体现了对异常情况的良好处理。
五、总结
本文详细介绍了JsonPathUtil
工具类的实现原理和应用。通过逐步解析代码,我们了解到:
- 如何解析复杂的JSON路径表达式,包括嵌套属性和数组元素。
- 使用
ObjectMapper
将JSON字符串转换为可操作的Java对象。 - 通过反射和类型检查,实现了对
Map
和普通Java对象的字段访问。
优点:
- 轻量级:不依赖于第三方库,适合对JSON路径解析需求不复杂的场景。
- 易于理解和扩展:代码简洁明了,方便根据需求进行定制。
不足:
- 功能有限:不支持复杂的表达式,如过滤条件、通配符等。
- 性能考虑:对于大规模的JSON数据和高并发场景,可能需要优化或选择性能更优的方案。
建议:
- 对于简单的JSON解析需求,可以直接使用
JsonPathUtil
工具类。 - 如果需要更高级的JSON路径功能,建议使用专业的JSON路径解析库,如Jayway的JsonPath。
- JsonPath 开源地址:https://github.com/json-path/JsonPath
- 在线语法检查:https://jsonpath.com/
六、后记
“慢慢学,不要停。”在编程的道路上,理解每一段代码背后的原理,都能让我们走得更远。希望通过本文的讲解,能帮助到有需要的读者,加深对JSON解析和Java反射的理解。
感谢阅读!