基于JsonPath解析JSON
一、JsonPath 说明
JsonPath是一种用于读取JSON文档的JavaDSL,能够方便快捷的解析复杂逻辑的Json。GitHub:JsonPath ,当然也可以 在线使用。
二、JsonPath 语法
1. 常用操作符
Operator | Description |
---|---|
$ | 要查询的根元素。表示所有表达式的开始路径 |
@ | 用在筛选器,表示当前节点 |
* | 作为通配符,匹配所有成员 |
.. | 子递归通配符,匹配成员的所有子元素 |
.<name> | .孩子节点名称 |
['<name>' (, '<name>')] | 括号中标注的一个或多个孩子节点 |
[<number> (, <number>)] | 括号中标注一个或多个孩子节点的下标(索引),从0开始 |
[start:end] | 表示从start下标开始到 end下标结束(不包括 end 下标)孩子节点 |
[?(<expression>)] | 过滤表达式。表达式的计算结果必须为布尔值 |
2. 常用过滤器操作符
Operator | Description |
---|---|
== | 左侧等于右侧。但是 1 不等于 ‘1’ |
!= | 左侧不等于右侧 |
< | 左侧小于右侧 |
<= | 左侧小于等于右侧 |
> | 左侧大于右侧 |
>= | 左侧大于等于右侧 |
=~ | 左侧正则匹配右侧 |
in | 左侧存在于右侧集合 |
nin | 左侧不存在于右侧集合 |
size | 左侧(数组或字符串)的大小应与右侧匹配 |
empty | 左侧(数组或字符串)应为空 |
subsetof | 左侧是右侧的子集。测试时发现无法调通 |
anyof | 左侧与右侧的交集。测试时发现无法调通 |
noneof | 左侧和右侧不相交的部分。测试时发现无法调通 |
3. 常用函数
Function | Description | Output type |
---|---|---|
min() | 返回数组的最小值 | Double |
max() | 返回数组的最大值 | Double |
avg() | 返回数组的平均值 | Double |
stddev() | 返回数组的标准方差 | Double |
length() | 返回数组的长度 | Integer |
sum() | 返回数组的总和 | Double |
keys() | 没搞明白这个函数是做什么的 | Set<E> |
concat(X) | 将数组中元素拼接成一个新的元素 | like input |
append(X) | 在 json 上添加元素。测试发现没有作用,提示没有该函数 | like input |
三、Java 语言实现JsonPath示例
1. 引入 maven 坐标
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
2. 数据准备
添加json文件 /json/ExampleJson.json
,内容如下所示:
{
"store": [
{
"name": "京东",
"label": "like",
"book": [
{
"category": "Java",
"author": "凯·S·霍斯特曼",
"title": "Java核心技术",
"price": 149,
"nation": "M"
},
{
"category": "Java",
"author": "Bruce Eckel",
"title": "Java编程思想",
"price": 54.13,
"nation": "M"
},
{
"category": "Java",
"author": "方腾飞",
"title": "Java并发编程的艺术",
"price": 48.12,
"nation": "C"
},
{
"category": "C++",
"author": "安东尼威廉姆斯",
"title": "C++并发编程实战",
"price": 137.00,
"nation": "M"
},
{
"category": "",
"author": "未知",
"title": "测试",
"price": 0,
"nation": "C"
}
],
"bicycle": {
"color": "red",
"price": 1999.99
},
"avg": 60
},
{
"name": "天猫",
"book": [
{
"category": "Python",
"author": "埃里克·马瑟斯 ",
"title": "Python从入门到实践",
"price": 107,
"nation": "M"
},
{
"category": "Python",
"author": "明日科技",
"title": "Python从入门到精通",
"price": 36.80,
"nation": "C"
}
]
}
],
"from": "互联网"
}
3. json解析示例
package com.study;
import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
/**
* @author ouyangrongtao
* @since 2022-04-20 22:28
*/
class JsonPathExampleTest {
private InputStream resource;
private static final Logger LOGGER = LoggerFactory.getLogger(JsonPathExampleTest.class);
@BeforeEach
void setUp() {
resource = JsonPathExample.class.getResourceAsStream("/json/ExampleJson.json");
}
@Test
void getAllBookListList() throws IOException {
Object books = JsonPath.read(resource, "$..book");
LOGGER.info("获取全部 book 节点:{}", books);
Assertions.assertNotNull(books);
}
@Test
void getAllBookList() throws IOException {
Object books = JsonPath.read(resource, "$..book[*]");
LOGGER.info("获取全部 book 节点:{}", books);
Assertions.assertNotNull(books);
}
@Test
void getAllAuthors() throws IOException {
Object authors = JsonPath.read(resource, "$..author");
LOGGER.info("获取全部 book 的 author:{}", authors);
Assertions.assertNotNull(authors);
}
@Test
void getJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0]");
LOGGER.info("获取京东store:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getBicycleFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].bicycle");
LOGGER.info("获取京东 store 下的 bicycle:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getFirstBookFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].book[0]");
LOGGER.info("获取京东 store 下的第一的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getLastBookFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].book[-1]");
LOGGER.info("获取京东 store 下的最后的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getFirstBookOfAuthorFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].book[0]['author', 'title']");
LOGGER.info("获取京东 store 下的第一的 book 的 author 和 title:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getFirstAndThirdBookOfAuthorFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].book[0, 2]");
LOGGER.info("获取京东 store 下的第一和第三的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getFirstToThirdBookOfAuthorFromJingdong() throws IOException {
Object o = JsonPath.read(resource, "$.store[0].book[0:3]");
LOGGER.info("获取京东 store 下的第一到第三的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getExistLabel() throws IOException {
Object o = JsonPath.read(resource, "$.store[?(@.label)]");
LOGGER.info("获取 store 下存在 label 的 store:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getNameEqual() throws IOException {
Object o = JsonPath.read(resource, "$.store[?(@.name=='京东')]");
LOGGER.info("获取 name=京东 的 store:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getPriceGT100() throws IOException {
Object o = JsonPath.read(resource, "$..book[?(@.price > 100)]");
LOGGER.info("获取 price 大于 100 的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getPriceGTAvg() throws IOException {
Object o = JsonPath.read(resource, "$..book[?(@.price > $.store[0].avg)]");
LOGGER.info("获取 price 大于 avg 的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getCategoryEmpty() throws IOException {
Object o = JsonPath.read(resource, "$..book[?(@.category == '')]");
LOGGER.info("获取 Java 和 Python 的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getJavaAndPython() throws IOException {
Object o = JsonPath.read(resource, "$..book[?(@.category in ['Java', 'Python'])]");
LOGGER.info("获取 Java 和 Python 的 book:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getMaxPrice() throws IOException {
Object o = JsonPath.read(resource, "max($..book[?(@.price)].price)");
LOGGER.info("获取 book 中最高的 price:{}", o);
Assertions.assertNotNull(o);
}
@Test
void getConcatPrice1() throws IOException {
Object o = JsonPath.read(resource, "concat($..book[?(@.price)].price)");
LOGGER.info("获取 book 中的price并拼接起来:{}", o);
Assertions.assertEquals(o, "14954.1348.12137.0010736.8");
}
@Test
void getConcatPrice2() throws IOException {
Object o = JsonPath.read(resource, "$..book[?(@.price)].price.concat()");
LOGGER.info("获取 book 中的price并拼接起来:{}", o);
Assertions.assertEquals(o, "14954.1348.12137.0010736.8");
}
}
四、JsonPath 其他功能
-
设置指定路径的json值
@Test void setValue() { String newJson = JsonPath.parse(resource).set("$.from", "oy").jsonString(); LOGGER.info("设置后的新json:{}", newJson); Assertions.assertNotNull(newJson); }
注意: 设置值的 path 必须存在,否则报
PathNotFoundException
异常。 -
支持缓存,支持自定义
com.jayway.jsonpath.spi.cache.LRUCache
(default, thread safe)
com.jayway.jsonpath.spi.cache.NOOPCache
(no cache)
自定义缓存使用CacheProvider.setCache()
-
JsonProvider SPI
JsonSmartJsonProvider
(default)
JacksonJsonProvider
JacksonJsonNodeJsonProvider
GsonJsonProvider
JsonOrgJsonProvider
JakartaJsonProvider