在一些特殊场景中,可能 一串json有几个甚至上万个节点,那么要去获取里面某一个节点或者说设置某个json指定key的值,那就非常麻烦了,一般我们是通过递归来进行获取,获取后还需要再通过递归进行遍历设置值,所以相当来说非常麻烦。是否有已有现成的工具进行设置呢?
注:使用当先请跳转到:注意点进行了解性能问题。
官网:https://goessner.net/articles/JsonPath/
github官网:https://github.com/json-path/JsonPath
jsonPath是一个非常强大的,可以通过规则、指定的格式获取或设置需要的json位置,通过jsonPath可以快速实现json自定义的位置获取或赋值。
过滤器是用于筛选数组的逻辑表达式。一个典型的过滤器将是[?(@.age > 18)],其中@表示正在处理的当前项目。可以使用逻辑运算符&&和||创建更复杂的过滤器。字符串文字必须用单引号或双引号括起来([?(@.color == 'blue')] 或者 [?(@.color == "blue")]).
操作符 | 描述 |
---|---|
== | left等于right(注意1不等于'1') |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
=~ | 匹配正则表达式[?(@.name =~ /foo.*?/i)] |
in | 左边存在于右边 [?(@.size in ['S', 'M'])] |
nin | 左边不存在于右边 |
size | (数组或字符串)长度 |
empty | (数组或字符串)为空 |
引入java的POM坐标
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
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数组的长度 |
String rule3 = FileTool.readFileString("D:\\workspace\\jsonpath_study\\src\\main\\java\\com.hong.test\\demo.json","UTF-8");
List<String> authors = JsonPath.read(rule3, "$.store.book[*].author");
System.out.println(Arrays.asList(authors));
结果
[["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]]
需要将下面的json,找到指定compType为http的节点,并且找到后,循环赋值到这个节点下面的chidren里面。比如
{
"compName":"xxx",
"identifyName":"order",
"compCode":"001",
"compType":"http",
"parentCompType":"service",
"parentCompCode":"",
"compIndex":"1",
"children":[
{
"compName":"版本",
"compType":"httpVersionNumber",
"compValue":"1.0",
"parentCompType":"http",
"compIndex":1,
"children":[
],
"currentPosition":""
},
{
"compName":"服务x",
"compType":"httpInstanceCode",
"compValue":"xx",
"parentCompType":"http",
"compIndex":2,
"children":[
],
"currentPosition":""
},
{
"compName":"xx列表",
"compType":"httpRequest",
"compValue":"",
"parentCompType":"http",
"compIndex":3,
"children":[
{
"compName":"用户名称",
"compType":"reqParams",
"compValue":"userName",
"parentCompType":"httpRequest",
"compIndex":1,
"children":[
{
"compName":"x",
"compType":"reqMapping",
"compValue":"order.userName",
"parentCompType":"reqParams",
"compIndex":1,
"children":[
],
"currentPosition":""
}
],
"currentPosition":""
}
] }
]
}
String rule2 = FileTool.readFileString("D:\\workspace\\jsonpath_study\\src\\main\\java\\com.hong.test\\b.json", "UTF-8");
JSONArray array = JSONObject.parseArray(JsonPath.read(rule2, "$..children[?(@.compType=='http')]").toString());
int i = 0;
while (i < 10000) {
i++;
for (Object o : array) {
JSONObject json = (JSONObject) o;
rule2 = JsonPath.parse(rule2).set("$..children[?(@.compCode=='" + json.get("compCode") + "')].children", "aa").jsonString();
}
}
System.out.println(rule2);
结果
{
"compName":"xxx",
"identifyName":"order",
"compCode":"001",
"compType":"http",
"parentCompType":"service",
"parentCompCode":"",
"compIndex":"1",
"children":“aa”
}
可以看到快速替换了,很方便。
上面为什么要用1万次,就是测试jsonPath的性能问题,可以发现,jsonpath如果在多个执行中会导致CPU突然间飙升20%~50%,这一点是需要注意的,如果你的json或者线上的环境本来cpu使用率就很高那就要注意是否符合你的场景,但是我这个json是非常非常长的,有一百多kb,一般情况下是不可能有这么长,所以还是需要根据自已的实际场景进行判断是否引用。切记 切记 切记
jsonPath底层也是通过递归的方式进行实现,有兴趣的同学可以进行研究底层源码。需要特别注意的是如果引用了jsonPath那么需要特别关注cpu的性能指标,jsonPath会在短时间内将执行的cpu突增20%~50%(大json),可能会导致cpu飙高,所以要特别注意是否适用当前场景。
参考地址:
https://zhuanlan.zhihu.com/p/30188199
http://www.ibloger.net/article/2329.html
https://www.leftso.com/blog/796.html