前言

在工作中,经常会遇到从一串 JSON 中提取一个或多个字段的情况,常用的做法就是将其反序列化为 JSONObject 对象,然后从对象中获取,如果是 JSONArray 就进行迭代获取,总之比较麻烦。可以使用 JsonPath 快速提取所需信息。

JSONPATH 简明语法

JsonPath 描述
$ 根节点
@ 当前节点
.or[] 子节点
.. 选择所有符合条件的节点
* 所有节点
[] 迭代器标示,如数组下标
[,] 支持迭代器中做多选
[start:end:step] 数组切片运算符
?() 支持过滤操作
() 支持表达式计算

尝试 JsonPath 前提准备

maven 工程引入 jsonpath 的依赖

1
2
3
4
5
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>

有人说,FastJson 自带了 JSONPath,为什么不用要用这个呢?我只能回 FastJson 确实很好用,但是 FastJson 的 JsonPath 是真难用!

JsonPath 简单入门版

给定一串 JSON 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"store": {
"book": [
{
"category": "文学",
"author": "曹雪芹",
"title": "红楼梦",
"price": 47.20
},
{
"category": "心理",
"author": "凯利·麦格尼格尔",
"title": "自控力",
"price": 30.20
},
{
"category": "励志",
"author": "史蒂芬·柯维",
"title": "高效能人士的七个习惯",
"isbn": "7515326395",
"price": 51
},
{
"category": "小说",
"author": "毛姆",
"title": "月亮与六便士",
"isbn": "7533936027",
"price": 19.50
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}

需要按照条件提取信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class JsonPathExample {
public static void main(String[] args) throws IOException {
// 读取上面的那串 JSON
File file = new File(JsonPathExample.class.getClassLoader().getResource("store.json").getPath());
String storeJson = FileUtils.readFileToString(file);

// 注意使用 JsonPath ,不要使用 FastJson 的 JSONPath
// 输出第一本书的作者
println(JsonPath.read(storeJson, "$.store.book[0].author").toString());
// 输出结果: 曹雪芹

// 输出所有书的作者
println(JsonPath.read(storeJson, "$.store.book[*].author").toString());
// 输出结果: ["曹雪芹","凯利·麦格尼格尔","史蒂芬·柯维","毛姆"]

// 输出分类为文学的书信息
println(JsonPath.read(storeJson, "$.store.book[?(@.category =='文学')]").toString());
// 输出结果: [{"author":"曹雪芹","price":47.2,"category":"文学","title":"红楼梦"}]

// 输出价格大于 50 的书
println(JsonPath.read(storeJson, "$.store.book[?(@.price > 50)]").toString());
// 输出结果: [{"author":"史蒂芬·柯维","price":51,"isbn":"7515326395","category":"励志","title":"高效能人士的七个习惯"}]

// 输出 book[*] 中包含 isbn 的书
println(JsonPath.read(storeJson, "$.store.book[?(@.isbn)]").toString());
// 输出结果: [{"author":"史蒂芬·柯维","price":51,"isbn":"7515326395","category":"励志","title":"高效能人士的七个习惯"},{"author":"毛姆","price":19.5,"isbn":"7533936027","category":"小说","title":"月亮与六便士"}]

// 输出 json 中所有的 price
println(JsonPath.read(storeJson, "$..price").toString());
// 输出结果: [19.95,47.2,30.2,51,19.5]
}

private static void println(String str) {
System.out.println(str);
}
}

以上基本上,简单提取需要 JSON 中的信息便已经足够了,以下为扩展内容,也是我在工作中使用到的。

JsonPath 复杂结构版

给定一串 JSON 如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
"bookId": 7333,
"volumeDetailList": [
{
"title": "卷一 恢弘世界",
"volumeId": 28585,
"chapterDetailList": [
{
"chapterId": 11719110,
"free": true,
"name": "第1章 上九天",
"price": 0,
"words": 1678,
"contentId": 2930434
},
{
"chapterId": 1719111,
"free": true,
"name": "第2章:揽月",
"price": 0,
"words": 2390,
"contentId": 2930444
}
]
},
{
"title": "卷二 在人家",
"volumeId": 285852,
"chapterDetailList": [
{
"chapterId": 1719120,
"free": false,
"name": "第3章:千年后之始",
"price": 19,
"words": 2989,
"contentId": 29540933
},
{
"chapterId": 17133111,
"free": false,
"name": "第4章:破冰而生",
"price": 30,
"words": 3409,
"contentId": 29540988
}
]
}
]
}

读取需要的相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class JsonPathExample2 {
public static void main(String[] args) throws IOException {
// 用于读取上面的那串 JSON
File file = new File(JsonPathExample2.class.getClassLoader().getResource("chapterlist.json").getPath());
String chapterlistJson = FileUtils.readFileToString(file);

// 得到书的所有卷名称
println(JsonPath.read(chapterlistJson, "$..title").toString());
// 输出结果: ["卷一 恢弘世界","卷二 在人家"]

// 得到书的所有章节名称
println(JsonPath.read(chapterlistJson, "$..name").toString());
// 输出结果: ["第1章 上九天","第2章:揽月","第3章:千年后之始","第4章:破冰而生"]

// 上面两个非常简单,下面这个需要注意 volumeDetailList 与 chapterDetailList 都是数组,且是数组嵌套
// 得到 chapterId 为 17133111 的 contentId
println(JsonPath.read(chapterlistJson, "$.volumeDetailList[*].chapterDetailList[?(@.chapterId == '17133111')].contentId").toString());
// 简化写法
println(JsonPath.read(chapterlistJson, "$..[?(@.chapterId == '17133111')].contentId").toString());
// 输出结果: 29540988
}

private static void println(String str) {
System.out.println(str);
}
}

常用JsonPath在线解析工具网站

这两个网站都可以在线写 JsonPath 语句,可以用来校验写的是否正确而不用去跑代码程序,比较方便

参考文章

小结

在工作中使用到的时候,也不熟悉 JsonPath 的语法,写出了比较复杂的 JsonPath,但是在写这篇博客的时候,又写了一般,发现了有很多自己写的 JsonPath 语句是可以精简的,比如倒数第二条 JsonPath 语句可以精简为它下面那句。

在输出的时候,可以发现自己认知中不足之处,写博客还是能给自己带来很多好处的,希望自己能够笔耕不息~