前言
我们在日常开发中,json几乎随处可见,但是繁琐的json,也给我们解析带来了很多烦恼的问题,多层级的解析,以及各种嵌套对象的解析,那有没有一种更简单的解析方式呢?
JsonPath
JsonPath与Json的关系,就跟XPath与XML的关系一样,JsonPath是设计来作为JSON 的路径语言,用于确定Json文档中某部分位置的语言,JsonPath的原理就是将Json数据转换为DOM树状结构,并提供在数据结构树种寻找节点以及各个节点的能力。详细的就不赘述,大家可以去官网翻阅查看并使用;https://github.com/json-path/JsonPath
表达式 | 结果 |
---|---|
$.store.book[*].author | 获取json中store下book下的所有author值 |
$..author | 获取所有json中所有author的值 |
$.store.* | 所有的东西,书籍和自行车 |
$.store..price | 获取json中store下所有price的值 |
$..book[2] | 获取json中book数组的第3个值 |
$..book[-2] | 倒数的第二本书 |
$..book[0,1] | 前两本书 |
$..book[:2] | 从索引0(包括)到索引2(排除)的所有图书 |
$..book[1:2] | 从索引1(包括)到索引2(排除)的所有图书 |
$..book[-2:] | 获取json中book数组的最后两个值 |
$..book[2:] | 获取json中book数组的第3个到最后一个的区间值 |
$..book[?(@.isbn)] | 获取json中book数组中包含isbn的所有值 |
$.store.book[?(@.price < 10)] | 获取json中book数组中price<10的所有值 |
$..book[?(@.price <= $['expensive'])] | 获取json中book数组中price<=expensive的所有值 |
$..book[?(@.author =~ /.*REES/i)] | 获取json中book数组中的作者以REES结尾的所有值(REES不区分大小写) |
$..* | 逐层列出json中的所有值,层级由外到内 |
$..book.length() | 获取json中book数组的长度 |
但是有的时候,我们并不需求这么复杂的解决过程,可能基本就是获取根节点中某个字段或者初始化一个json等等,我们可以在JsonPath的基础上封装一个简单的工具类,来提供便利性
AbstractExtJsonParse
import cn.gov.zcy.service.exp.ExtJsonParseException;
import com.google.common.base.Stopwatch;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* AbstractExtJsonParse
*
* @author Duansg
* @version 1.0
* @date 2021/11/17 下午8:41
*/
@Slf4j
public abstract class AbstractExtJsonParse {
/**
* 根路径
*/
protected static final String ROOT_PREFIX = "$";
/**
* 层级符号
*/
protected static final String SYMBOL_PREFIX = ".";
/**
* 空json
*/
protected static final String ROOT_EMPTY = "{}";
/**
* 获取解析耗时统计的时间单位
*
* @return
*/
protected abstract TimeUnit getTimeUnit();
/**
* 获取耗时日志开关
*
* @return
*/
protected abstract boolean getShowLog();
/**
* 设置扩展字段
*
* @param json
* 原json
* @param path
* 设置路径
* @param key
* 需要设置的key
* @param value
* 需要设置的值
* @return
* @throws ExtJsonParseException
*/
protected String basePut(String json, String path, Map<String, Object> values) throws ExtJsonParseException {
final boolean showLog = getShowLog();
final Stopwatch stopwatch = showLog ? Stopwatch.createStarted() : null;
try {
DocumentContext context = JsonPath.parse(json);
values.forEach((key, value) -> context.put(path, key, value));
return context.jsonString();
} catch (Exception e) {
throw new ExtJsonParseException(e.getMessage());
} finally {
if (showLog) {
log.info("设置完毕,json:{},path:{},values:{},耗时:{} ms", json, path, values,
stopwatch.elapsed(getTimeUnit()));
}
}
}
/**
* 基础解析
*
* @param json
* 需要解析的json
* @param path
* 需要获取的路径
* @param clazz
* 解析类型
* @param <T>
* @return
* @throws ExtJsonParseException
*/
protected <T> T baseParse(String json, String path, Class<T> clazz, boolean isCustomPath)
throws ExtJsonParseException {
final boolean showLog = getShowLog();
final Stopwatch stopwatch = showLog ? Stopwatch.createStarted() : null;
try {
DocumentContext parse = JsonPath.parse(json);
path = isCustomPath ? path : ROOT_PREFIX.concat(SYMBOL_PREFIX).concat(path);
return ObjectUtils.isEmpty(clazz) ? parse.read(path) : parse.read(path, clazz);
} catch (Exception e) {
throw new ExtJsonParseException(e.getMessage());
} finally {
if (showLog) {
log.info("解析完毕,json:{},path:{},ClassName:{},耗时:{} ms", json, path, clazz.getName(),
stopwatch.elapsed(getTimeUnit()));
}
}
}
/**
* 自定义设值
*
* @param json
* 目标json
* @param function
* 自定义函数
* @return
* @throws ExtJsonParseException
*/
public String putInCustom(String json, Function<DocumentContext, DocumentContext> function)
throws ExtJsonParseException {
final boolean showLog = getShowLog();
final Stopwatch stopwatch = showLog ? Stopwatch.createStarted() : null;
try {
// DocumentContext为jsonpath上下文
return function.apply(JsonPath.parse(json)).jsonString();
} catch (Exception e) {
throw new ExtJsonParseException(e.getMessage());
} finally {
if (showLog) {
log.info("设置完毕,json:{},耗时:{} ms", json, stopwatch.elapsed(getTimeUnit()));
}
}
}
/**
* 自定义获取操作
*
* @param json
* 目标json
* @param function
* 自定义函数
* @param <T>
* @return
* @throws ExtJsonParseException
*/
public <T> T getForCustom(String json, Function<DocumentContext, T> function) throws ExtJsonParseException {
if (StringUtils.isBlank(json)) {
return null;
}
if (StringUtils.isBlank(json)) {
throw new ExtJsonParseException("目标参数[json]不能为空");
}
final boolean showLog = getShowLog();
final Stopwatch stopwatch = showLog ? Stopwatch.createStarted() : null;
try {
// DocumentContext为jsonpath上下文
return function.apply(JsonPath.parse(json));
} catch (Exception e) {
throw new ExtJsonParseException(e.getMessage());
} finally {
if (showLog) {
log.info("解析完毕,json:{},耗时:{} ms", json, stopwatch.elapsed(getTimeUnit()));
}
}
}
}
ExtJsonParseSupport
import cn.gov.zcy.service.exp.ExtJsonParseException;
import cn.gov.zcy.service.support.base.AbstractExtJsonParse;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* ExtJsonParseSupport
*
* @author Duansg
* @version 1.0
* @date 2021/11/17 下午4:55
* @see cn.gov.zcy.item.test.common.ExtJsonParseSupportTest
*/
@Slf4j
@Builder
public final class ExtJsonParseSupport extends AbstractExtJsonParse {
// 具体用法请见@see cn.gov.zcy.item.test.common.ExtJsonParseSupportTest
/**
* 解析耗时统计-时间单位
*/
@Builder.Default
private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
/**
* 耗时日志开关
*/
@Builder.Default
private boolean showLog = false;
/**
* 获取根路径下的指定key的值,返回自定义类型
*
* @param json
* 目标json
* @param key
* 指定键
* @param valueType
* 对应值的类型
* @param <T>
* @return
* @throws ExtJsonParseException
*/
public <T> T getForRoot(String json, String key, Class<T> valueType) throws ExtJsonParseException {
if (StringUtils.isBlank(json)) {
return null;
}
if (StringUtils.isBlank(key)) {
throw new ExtJsonParseException("目标参数[key]不能为空");
}
if (ObjectUtils.isEmpty(valueType)) {
throw new ExtJsonParseException("目标参数[valueType]不能为空");
}
return super.baseParse(json, key, valueType, false);
}
/**
* 获取根路径下的指定key的值,返回自定义类型
*
* @param json
* 目标json
* @param key
* 指定键
* @param valueType
* 对应值的类型
* @param <T>
* @return
*/
public <T> T getForRootIgnoreExcep(String json, String key, Class<T> valueType) {
try {
if (StringUtils.isBlank(json) || StringUtils.isBlank(key) || ObjectUtils.isEmpty(valueType)) {
return null;
}
return super.baseParse(json, key, valueType, false);
} catch (Exception e) {
log.warn("json-path解析异常,此异常已忽略并返回null值,解析数据为:{},key为:{},type:{}", json, key, valueType.getName());
return null;
}
}
/**
* 获取根路径下的指定key的值,返回自定义类型
*
* @param json
* 目标json
* @param key
* 指定键
* @param valueType
* 对应值的类型
* @param <T>
* @return
*/
public <T> T getForContainsRootIgnoreExcep(String json, String key, Class<T> valueType) {
try {
if (StringUtils.isBlank(json) || StringUtils.isBlank(key) || ObjectUtils.isEmpty(valueType)) {
return null;
}
if (!json.contains(key)) {
log.warn("json-path解析错误告警,此json中不包含key的信息,解析数据为:{},key为:{}", json, key);
return null;
}
return super.baseParse(json, key, valueType, false);
} catch (Exception e) {
log.warn("json-path解析异常,此异常已忽略并返回null值,解析数据为:{},key为:{},type:{}", json, key, valueType.getName());
return null;
}
}
/**
* 获取根路径下的指定key的值,返回object
*
* @param json
* 目标json
* @param key
* 指定键
* @return
* @throws ExtJsonParseException
*/
public Object getForRoot(String json, String key) throws ExtJsonParseException {
if (StringUtils.isBlank(json)) {
return null;
}
if (StringUtils.isBlank(key)) {
throw new ExtJsonParseException("目标参数[key]不能为空");
}
return super.baseParse(json, key, null, false);
}
/**
* 获取指定路径下的指定key的值,返回object
*
* @param json
* 目标json
* @param path
* 指定路径
* @return
* @throws ExtJsonParseException
*/
public Object getForPath(String json, String path) throws ExtJsonParseException {
if (StringUtils.isBlank(json)) {
return null;
}
if (StringUtils.isBlank(path)) {
throw new ExtJsonParseException("目标参数[path]不能为空");
}
return super.baseParse(json, path, null, true);
}
/**
* 获取指定路径下的指定key的值,返回自定义类型
*
* @param json
* 目标json
* @param path
* 指定路径
* @return
* @throws ExtJsonParseException
*/
public <T> T getForPath(String json, String path, Class<T> valueType) throws ExtJsonParseException {
if (StringUtils.isBlank(json)) {
return null;
}
if (StringUtils.isBlank(path)) {
throw new ExtJsonParseException("目标参数[json,path,key]不能为空");
}
return super.baseParse(json, path, valueType, true);
}
/**
* 根路径下设置值,支持目标json为空
*
* @param json
* 目标json
* @param key
* 设置键
* @param value
* 设置值
* @return
* @throws ExtJsonParseException
*/
public String putInRoot(String json, String key, Object value) throws ExtJsonParseException {
if (StringUtils.isBlank(key)) {
throw new ExtJsonParseException("目标参数[key]不能为空");
}
if (ObjectUtils.isEmpty(value)) {
throw new ExtJsonParseException("目标值[value]不能为空");
}
// 为空的话设置默认值
json = StringUtils.isBlank(json) ? ROOT_EMPTY : json;
return super.basePut(json, ROOT_PREFIX, new HashMap<String, Object>() {
private static final long serialVersionUID = -6493253680889168520L;
{
put(key, value);
}
});
}
/**
* 根路径下设置值,支持目标json为空
*
* @param json
* 目标json
* @param key
* 设置键
* @param value
* 设置值
* @return
* @throws ExtJsonParseException
*/
public String putInRootForMap(String json, Map<String, Object> values) throws ExtJsonParseException {
if (CollectionUtils.isEmpty(values)) {
throw new ExtJsonParseException("目标参数[values]不能为空");
}
// 为空的话设置默认值
json = StringUtils.isBlank(json) ? ROOT_EMPTY : json;
return super.basePut(json, ROOT_PREFIX, values);
}
/**
* 指定路径下设置值,不支持目标json为空
*
* @param json
* 目标json
* @param path
* 目标路径
* @param key
* 设置键
* @param value
* 设置值
* @return
* @throws ExtJsonParseException
*/
public String putInPath(String json, String path, String key, Object value) throws ExtJsonParseException {
if (StringUtils.isBlank(json) || StringUtils.isBlank(key) || StringUtils.isBlank(path)) {
throw new ExtJsonParseException("目标参数[json,key,path]不能为空");
}
if (ObjectUtils.isEmpty(value)) {
throw new ExtJsonParseException("目标值[value]不能为空");
}
return super.basePut(json, path, new HashMap<String, Object>() {
private static final long serialVersionUID = -6493253680889168520L;
{
put(key, value);
}
});
}
@Override
public TimeUnit getTimeUnit() {
return this.timeUnit;
}
@Override
protected boolean getShowLog() {
return this.showLog;
}
}
Test Unit
import cn.gov.zcy.service.exp.ExtJsonParseException;
import cn.gov.zcy.service.support.ExtJsonParseSupport;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
/**
* ExtJsonParseSupportTest
*
* @author Duansg
* @version 1.0
* @date 2021/11/18 上午01:12
*/
@Slf4j
public class ExtJsonParseSupportTest {
// 不要格式化此类!!!
/**
* {
* "from": 0,
* "size": 10,
* "timeout": "5000ms",
* "query": {
* "bool": {
* "filter": [
* {
* "term": {
* "currentAuditOrgId": {
* "value": 10001000259856,
* "boost": 1.0
* }
* }
* },
* {
* "terms": {
* "status": [
* 0,
* 1
* ],
* "boost": 1.0
* }
* },
* {
* "terms": {
* "currentAuditCode": [
* "zcy.agreement.center.goods.audit.prsecond",
* "zcy.agreement.center.goods.audit.prfirst",
* "zcy.agreement.center.goods.audit.prUnfreezeFirst",
* "zcy.agreement.center.goods.audit.prUnfreezeThird",
* "zcy.agreement.center.goods.audit.prUnfreezeSecond",
* "zcy.agreement.center.goods.audit.prthird"
* ],
* "boost": 1.0
* }
* }
* ],
* "adjust_pure_negative": true,
* "boost": 1.0
* }
* },
* "sort": [
* {
* "gmtApplied": {
* "order": "asc"
* }
* }
* ],
* "aggregations": {
* "aggCurrentAuditCode": {
* "terms": {
* "field": "currentAuditCode",
* "size": 12,
* "min_doc_count": 1,
* "shard_min_doc_count": 0,
* "show_term_doc_count_error": false,
* "execution_hint": "global_ordinals",
* "order": [
* {
* "_count": "desc"
* },
* {
* "_key": "asc"
* }
* ]
* }
* }
* }
* }
*/
private String json = "{\"from\":0,\"size\":10,\"timeout\":\"5000ms\",\"query\":{\"bool\":{\"filter\":[{\"term\":{\"currentAuditOrgId\":{\"value\":10001000259856,\"boost\":1.0}}},{\"terms\":{\"status\":[0,1],\"boost\":1.0}},{\"terms\":{\"currentAuditCode\":[\"zcy.agreement.center.goods.audit.prsecond\",\"zcy.agreement.center.goods.audit.prfirst\",\"zcy.agreement.center.goods.audit.prUnfreezeFirst\",\"zcy.agreement.center.goods.audit.prUnfreezeThird\",\"zcy.agreement.center.goods.audit.prUnfreezeSecond\",\"zcy.agreement.center.goods.audit.prthird\"],\"boost\":1.0}}],\"adjust_pure_negative\":true,\"boost\":1.0}},\"sort\":[{\"gmtApplied\":{\"order\":\"asc\"}}],\"aggregations\":{\"aggCurrentAuditCode\":{\"terms\":{\"field\":\"currentAuditCode\",\"size\":12,\"min_doc_count\":1,\"shard_min_doc_count\":0,\"show_term_doc_count_error\":false,\"execution_hint\":\"global_ordinals\",\"order\":[{\"_count\":\"desc\"},{\"_key\":\"asc\"}]}}}}";
@Test
public void _获取根路径下的指定key的值_指定类型() {
try {
Integer value = ExtJsonParseSupport.builder().build().getForRoot(json, "fraaaaom", Integer.class);
log.info("_获取根路径下的指定key的值_指定类型:{}", value);
// 00:11:07.589 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _获取根路径下的指定key的值_指定类型:0
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _获取根路径下的指定key的值_Object() {
try {
Object value = ExtJsonParseSupport.builder().build().getForRoot(json, "timeout");
String printValue = (String) value;
log.info("_获取根路径下的指定key的值_Object:{}", printValue);
// 00:14:27.221 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _获取根路径下的指定key的值_Object:5000ms
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _获取指定路径下的指定key的值_Object() {
try {
/*
Object v1 = ExtJsonParseSupport.builder().build().getForPath(json, "$.query.bool.filter");
Object v1_1 = ExtJsonParseSupport.builder().build().getForPath(json, "$.query.bool.filter[*]");
List printValue = (List)v1;
List printValue = (List)v1_1;
log: _获取指定路径下的指定key的值_Object:[{"term":{"currentAuditOrgId":{"value":10001000259856,"boost":1.0}}},{"terms":{"status":[0,1],"boost":1.0}},{"terms":{"currentAuditCode":["zcy.agreement.center.goods.audit.prsecond","zcy.agreement.center.goods.audit.prfirst","zcy.agreement.center.goods.audit.prUnfreezeFirst","zcy.agreement.center.goods.audit.prUnfreezeThird","zcy.agreement.center.goods.audit.prUnfreezeSecond","zcy.agreement.center.goods.audit.prthird"],"boost":1.0}}]
*/
// -----------------
/*
Object v2 = ExtJsonParseSupport.builder().build().getForPath(json, "$.query.bool.filter[1]");
Map printValue = (Map)v2;
log:00:21:46.540 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _获取指定路径下的指定key的值_Object:{terms={status=[0,1], boost=1.0}}
*/
Object v2 = ExtJsonParseSupport.builder().build().getForPath(json,
"$.query.bool.filter[0].term.currentAuditOrgId.value");
// 00:28:41.298 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _获取指定路径下的指定key的值_Object:10001000259856
log.info("_获取指定路径下的指定key的值_Object:{}", v2);
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _获取指定路径下的指定key的值_自定义类型() {
try {
// 其他用法同上 @see _获取指定路径下的指定key的值_Object testMethod
Long value = ExtJsonParseSupport.builder().build().getForPath(json,
"$.query.bool.filter[0].term.currentAuditOrgId.value", Long.class);
log.info("_获取指定路径下的指定key的值_自定义类型:{}", value);
// 00:31:57.411 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _获取指定路径下的指定key的值_自定义类型:10001000259856
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void 自定义获取操作() {
try {
Long value =
ExtJsonParseSupport.builder().build().getForCustom(json, documentContext -> {
Object v1 = documentContext.read("$.query.bool.filter[0].term.currentAuditOrgId.value");
Long v2 =
documentContext.read("$.query.bool.filter[0].term.currentAuditOrgId.value", Long.class);
log.info("自定义获取操作_v1:{}", v1);
log.info("自定义获取操作_v2:{}", v2);
return v2;
});
log.info("自定义获取操作:{}", value);
/*
00:35:22.924 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - 自定义获取操作_v1:10001000259856
00:35:22.924 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - 自定义获取操作_v2:10001000259856
00:35:22.924 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - 自定义获取操作:10001000259856
*/
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _根路径下设置值() {
try {
String v1 = ExtJsonParseSupport.builder().build().putInRoot("", "Duansg_v1", "hhhh");
String v2 = ExtJsonParseSupport.builder().build().putInRoot(null, "Duansg_v2", "gggg");
log.info("_根路径下设置值,v1:{},v2:{}", v1, v2);
/*
00:40:39.375 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - _根路径下设置值,v1:{"Duansg_v1":"hhhh"},v2:{"Duansg_v2":"gggg"}
*/
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _指定路径下设置值() {
try {
String tempJson = "{\"item\":{\"name\":\"hhh\"}}";
String s = ExtJsonParseSupport.builder().build().putInPath(tempJson, "$", "Duansg_v1", "hhhh");
log.info(s);
/*
00:45:12.969 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest -
{"item":{"name":"hhh"},"Duansg_v1":"hhhh"}
*/
String s1 = ExtJsonParseSupport.builder().build().putInPath(s, "$.item", "Duansg_v2", "ggg");
log.info(s1);
/*
00:45:12.970 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest -
{"item":{"name":"hhh","Duansg_v2":"ggg"},"Duansg_v1":"hhhh"}
*/
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
@Test
public void _自定义设值() {
String tempJson = "{\"item\":{\"name\":\"hhh\"}}";
try {
String s = ExtJsonParseSupport.builder().build().putInCustom(tempJson, documentContext -> {
documentContext.put("$", "Duansg_v1", "hhhh");
documentContext.put("$.item", "Duansg_v2", "ggg");
return documentContext;
});
log.info(s);
/*
00:48:42.546 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest
- {"item":{"name":"hhh","Duansg_v2":"ggg"},"Duansg_v1":"hhhh"}
*/
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
/**
* 以下为成员变量的使用方法
*/
@Test
public void _耗时与开关() {
String tempJson = "{\"item\":{\"name\":\"hhh\"}}";
try {
// 如果解析比较大的json,建议将开关在调用方设置动态配置进行获取,
String s = ExtJsonParseSupport.builder().timeUnit(TimeUnit.MILLISECONDS).showLog(true).build()
.putInCustom(tempJson, documentContext -> {
documentContext.put("$", "Duansg_v1", "hhhh");
documentContext.put("$.item", "Duansg_v2", "ggg");
return documentContext;
});
log.info(s);
/*
01:09:25.266 [main] INFO cn.gov.zcy.service.support.base.AbstractExtJsonParse - 设置完毕,json:{"item":{"name":"hhh"}},耗时:133 ms
01:09:25.266 [main] INFO cn.gov.zcy.item.test.common.ExtJsonParseSupportTest - {"item":{"name":"hhh","Duansg_v2":"ggg"},"Duansg_v1":"hhhh"}
*/
} catch (ExtJsonParseException e) {
e.printStackTrace();
}
}
}