环境:SpringBoot3.4.0
1. 简介
你是否遇到过需要从一个复杂的JSON结构中提取特定信息的场景?
当你面对一个嵌套层级很多、结构复杂的JSON对象时,你是否觉得逐一创建对应的Java实体类(或者是Map)来接收这些数据既繁琐又低效?
在进行单元测试或模拟请求时,你是否需要构建或修改JSON数据?
在测试过程中,我们可能需要构建特定的JSON请求体或修改响应数据。有没有一种工具可以帮助我们轻松地完成这些操作?
答案是肯定的!有一种名为JSONPath的工具,它提供了一种类似于XPath的表达式语言,用于在JSON文档中查询和操作数据。JSONPath不仅能够高效地定位和提取JSON中的特定信息,还支持数据过滤、转换和更新等操作。
2. 实战案例
2.1 准备环境
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
如果你是基于Spring Boot环境,那么你不需要指定版本,Spring Boot已经帮我们管理了该组件的版本。截至撰写该篇文章时,JSONPath的最新版本是2.9.0。
说明:JsonPath表达式总是以与XPath表达式在XML文档中结合使用相同的方式来引用JSON结构。在JsonPath中,“根成员对象”无论是对象还是数组,总是被引用为$。
2.2 语法介绍
JsonPath 表达式可以使用点符号"."
$.store.book[0].title
也可以使用括号表示法
$['store']['book'][0]['title']
操作符
操作 | 说明 |
$ | 查询的根元素,所有路径表达式的起点 |
@ | 由过滤器谓词处理的当前节点 |
* | 通配符,在需要名称或数字的位置可用 |
.. | 深度扫描,在需要名称的位置可用 |
.<name> | 点表示法的子节点 |
['<name>' (, '<name>')] | 方括号表示法的子节点或子节点集合 |
[<number> (, <number>)] | 数组索引或索引集合 |
[start:end] | 数组切片操作符 |
[?(<expression>)] | 过滤器表达式,表达式必须计算为布尔值 |
函数
函数可以在路径的末尾被调用——函数的输入是路径表达式的输出。函数的输出由函数本身决定。支持如下的函数:
过滤操作
过滤器是用于筛选数组的逻辑表达式。一个典型的过滤器可能是[?(@.age > 18)],其中@代表当前正在处理的项。可以使用逻辑运算符&&和||创建更复杂的过滤器。字符串字面量必须用单引号或双引号括起来(例如[?(@.color == 'blue')]或[?(@.color == "blue")])。支持如下过滤:
操作符 | 说明 |
== | 左值等于右值(注意1不等于'1') |
!= | 左值不等于右值 |
< | 左值小于右值 |
<= | 左值小于或等于右值 |
> | 左值大于右值 |
>= | 左值大于或等于右值 |
=~ | 左值匹配正则表达式 [?(@.name =~ /foo.*?/i)] |
in | 左值存在于右值中 [?(@.size in ['S', 'M'])] |
nin | 左值不存在于右值中 |
subsetof | 左值是右值的子集 [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | 左值与右值有交集 [?(@.sizes anyof ['M', 'L'])] |
noneof | 左值与右值无交集 [?(@.sizes noneof ['M', 'L'])] |
size | 左值(数组或字符串)的大小应与右值匹配 |
empty | 左值(数组或字符串)应为空 |
接下来,我们介绍具体的使用示例。
2.3 使用示例
准备json数据
{
"store": {
"book": [
{
"category": "Java",
"author": "张三",
"title": "Java从入门到放弃",
"price": 88.6
},
{
"category": "Spring",
"author": "Pack",
"title": "Spring从入门到精通",
"price": 99.8
},
{
"category": "Java",
"author": "李四",
"title": "多线程并发编程",
"isbn": "1-9527-6688-9",
"price": 66.9
},
{
"category": "Spring",
"author": "Pack",
"title": "Spring Boot3实战案例100讲",
"isbn": "6-9527-7799-2",
"price": 70
}
],
"bicycle": {
"color": "red",
"price": 666
}
},
"expensive": 10
}
层级格式如下:
接下来,我们将在代码中通过jsonpath来操作上面的json数据。
读取json内容
String json = new ClassPathResource("data.json")
.getContentAsString(StandardCharsets.UTF_8) ;
下面都将基于上面读取到的json数据进行操作。
获取所有图书的作者
List<String> list = JsonPath.parse(json).read("$.store.book[*].author");
System.err.println("Authors: " + list) ;
// 输出结果
Authors: ["张三","Pack","李四","Pack"]
获取所有作者
List<String> list = JsonPath.parse(json).read("$..author") ;
System.err.println("Authors: " + list) ;
// 输出结果
Authors: ["张三","Pack","李四","Pack"]
获取store节点下的所有数据
Object result = JsonPath.parse(json).read("$.store.*") ;
System.err.println(result) ;
输出结果如下:
类型是:net.minidev.json.JSONArray
获取所有价格price
/**获取store节点下的所有price*/
List<Double> prices = JsonPath.parse(json).read("$.store..price") ;
System.err.println(prices) ;
// 输出结果
[88.6,99.8,66.9,70,666]
获取指定索引的书
/**获取第三本书*/
Object result = JsonPath.parse(json).read("$..book[2]") ;
System.err.println(result) ;
/**输出结果; 类型:net.minidev.json.JSONArray*/
[{"category":"Java","author":"李四","title":"多线程并发编程","isbn":"1-9527-6688-9","price":66.9}]
result = JsonPath.parse(json).read("$..book[0,1]") ;
System.err.println(result) ;
// 输出结果
[
{"category":"Java","author":"张三","title":"Java从入门到放弃","price":88.6},
{"category":"Spring","author":"Pack","title":"Spring从入门到精通","price":99.8}
]
所有带有ISBN编号的书
/**所有带有ISBN编号的书*/
Object result = JsonPath.parse(json).read("$..book[?(@.isbn)]") ;
System.err.println(result) ;
// 输出结果
[
{"category":"Java","author":"李四","title":"多线程并发编程","isbn":"1-9527-6688-9","price":66.9},
{"category":"Spring","author":"Pack","title":"Spring Boot3实战案例100讲","isbn":"6-9527-7799-2","price":70}
]
获取所有价格小于70元的图书
/**获取所有价格小于70元的图书*/
Object result = JsonPath.parse(json).read("$.store.book[?(@.price < 70)]") ;
System.err.println(result) ;
// 输出结果
[
{"category":"Java","author":"李四","title":"多线程并发编程","isbn":"1-9527-6688-9","price":66.9}
]
获取图书的个数
// 获取图书的个数
Integer count = JsonPath.parse(json).read("$..book.length()") ;
System.err.println(count) ;
// 输出结果
4
自定义过滤条件
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
// 自定义filter
Filter cheapFictionFilter = filter(
where("category").is("Java").and("price").lte(70D)
);
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", cheapFictionFilter);
System.err.println(books) ;
// 输出结果
[
{"category":"Java","author":"李四","title":"多线程并发编程","isbn":"1-9527-6688-9","price":66.9}
]
修改数据
// 修改数据
String newJson = JsonPath.parse(json)
.set("$['store']['book'][0]['author']", "pack_xg")
.jsonString();
System.out.println(newJson) ;
输出结果:
图片