JsonPathUtil
的实现与应用作者:zibo 日期:2024/11/25 口号:慢慢学,不要停。
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")); // 输出1
System.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
,获取嵌套结构中指定员工的姓名。首先,定义了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字符串的解析。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
。ObjectMapper
将JSON字符串解析为Map<String, Object>
类型的rootObject
。.
分隔符,将表达式拆分为多个部分parts
,然后逐一处理每个部分。currentObject
,直到获取最终的值。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
获取属性值。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
的实例,如果是,则返回对应索引的元素。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
,直接获取对应键的值。setAccessible(true)
)。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")); // 输出1
System.out.println("测试空值处理: " + getValue(jsonString, "userInfo.notExist")); // 输出null
} catch (Exception e) {
System.err.println("处理异常: " + e.getMessage());
e.printStackTrace();
}
}
解析:
为了更清晰地展示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
工具类的实现原理和应用。通过逐步解析代码,我们了解到:
ObjectMapper
将JSON字符串转换为可操作的Java对象。Map
和普通Java对象的字段访问。优点:
不足:
建议:
JsonPathUtil
工具类。“慢慢学,不要停。”在编程的道路上,理解每一段代码背后的原理,都能让我们走得更远。希望通过本文的讲解,能帮助到有需要的读者,加深对JSON解析和Java反射的理解。
感谢阅读!