概述
JsonPath,GitHub是一种简单的方法来提取给定JSON文档的部分内容,提供类似正则表达式的语法来解析JSON文档。
特性
入门
引入如下Maven依赖:
<dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId>
</dependency>
基本使用:
JsonPath.read("", "");
需要传入两个String类型的参数,第一个参数为一个JSON String,第二个参数为JsonPath表达式。对应的源码为:
public static <T> T read(String json, String jsonPath, Predicate... filters) {return (new ParseContextImpl()).parse(json).read(jsonPath, filters);
}
第三个参数Predicate,表示谓词,在进阶部分会详细讲解。
查看com.jayway.jsonpath.JsonPath
源码,不难发现,第一个参数可以替换成Object、File、URL、InputStream类型的JSON,即可以直接读取一个JSON文件或网络资源等。
返回类型可以是用泛型定义的任意对象(<T> T
),也可以是一个DocumentContext(用得不多,可以直接忽略)。
JsonPath表达式
从上面的简介,不难得知,使用好JsonPath的基础条件是熟悉JsonPath表达式:
- JsonPath中的根成员对象始终称为
$
,无论是对象JSON Object还是数组JSON Array - JsonPath表达式支持点表示法
- 也支持括号表示法,繁琐,不建议使用
- 解析JSON Array时,支持数组表示法,索引从0开始
- 数组表示法,和Python 数组语法几乎没有差别
*
表示所有节点..
表示选择所有符合条件的节点?()
表示过滤操作@
表示当前节点
如何校验JsonPath表达式的合法性呢?
JsonPath.compile("$..");
使用IDEA的话,编译器会给出warning,执行mvn compile可以成功。运行这一行代码,JsonPath给出报错:InvalidPathException: Path must not end with a '.' or '..'
函数支持
函数可以在路径的尾部调用,函数的输出是路径表达式的输出,该函数的输出是由函数本身所决定的。
函数 | 描述 | 输出 |
---|---|---|
min() | 返回数值类型数组的最小值 | Double |
max() | 返回数值类型数组的最大值 | Double |
avg() | 返回数值类型数组的平均值 | Double |
stddev() | 返回数值类型数组的标准偏差值 | Double |
length() | 返回数值类型数组的长度 | Integer |
过滤器运算符
?()
,表示过滤器,用于筛选数组的逻辑表达式。一个典型的过滤器将是[?(@.age > 18)]
,@
表示正在处理的当前项目。可使用逻辑运算符&&
和||
来创建更复杂的过滤器。字符串文字必须用单引号或双引号括起来:[?(@.color == 'blue')]
或[?(@.color == "blue")]
。
操作符 | 描述 |
---|---|
== | left等于right(注意1不等于’1’) |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
=~ | 匹配正则表达式[?(@.name =~ /foo.*?/i)] |
in | 左边存在于右边 [?(@.size in [‘S’, ‘M’])] |
nin | 左边不存在于右边 |
size | 数组或字符串长度 |
empty | 数组或字符串为空 |
实战
示例JSON文档:
{"store": {"book": [{"category": "reference","author": "Nigel Rees","title": "Sayings of the Century","price": 8.95},{"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
}
很简单的一个JSON文档,有两个键值对,其中store表示商场,卖book和bicycle;expensive=10。
$.store.book[0].title
表示获取第一本书的标题,测试代码:
log.info(JsonPath.read(jsonStr, "$.store.book[0].title"));
输出:Sayings of the Century
。
使用括号表示法,$['store']['book'][0]['title']
,输出同样是Sayings of the Century
。
$..book.length()
表示获取book这个Array的个数,输出2,测试代码略。
$..book[0,1]
表示获取第一、二两本书。
$..book[-2:]
表示获取最后两本书。
$..book[:1]
表示获取从索引0开始的书籍,索引1不算在内。
$..book[1:2]
表示获取从索引1开始的书籍,索引2不算在内。
$..book[1:]
表示获取第二本(索引1开始)到最后一本书。
$.store.book[*].author
表示获取所有book的作者,输出是一个列表。使用..
的话,$..author
也表示获取所有作者,输出列表。
$..book[?(@.isbn)]
表示获取JSON中book Array里包含isbn编号的书。
$.store.book[?(@.isbn)].author
表示获取JSON中book Array里包含isbn编号的书的作者。
$.store.book[?(@.price < 10)]
表示获取JSON中book Array里售价低于10的书。
$..book[?(@.price < $['expensive'])]
表示获取JSON中book Array里售价低于expensive
的书。
支持的常用表达式
JsonPath | 描述 |
---|---|
$ | 根节点 |
@ | 当前节点 |
. or[] | 子节点 |
.. | 选择所有符合条件的节点 |
* | 所有节点 |
[] | 迭代器标示,如数组下标 |
[,] | 支持迭代器中做多选 |
[start:end:step] | 数组切片运算符 |
?() | 支持过滤操作 |
() | 支持表达式计算 |
进阶
Exception
以json-path-2.9.0版本为例,共定义8个Exception:
- InvalidCriteriaException
- InvalidJsonException
- InvalidModificationException
- InvalidPathException
- JsonPathException
- PathNotFoundException
- ValueCompareException
- EvaluationAbortException
- MappingException
谓词
谓词,谓语,也有翻译成断言的,即Predicate,JsonPath中的过滤器谓词有三种:
- 内联谓词
- 过滤器谓词
- 自定义谓词
内联谓词
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?(@.price < 10)]");
可使用&&
和||
结合多个谓词[?(@.price < 10 && @.category == 'fiction')]
。使用!
否定一个谓词[?(!(@.price < 10 && @.category == 'fiction'))]
。
过滤谓词
使用Filter API构建:
import static com.jayway.jsonpath.JsonPath.parse;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
Filter filter = filter(where("category").is("fiction").and("price").lte(10D)
);
List<Map<String, Object>> books = parse(json).read("$.store.book[?]", filter);
占位符?
为路径中的过滤器。当提供多个过滤器时,它们按照占位符数量与提供的过滤器数量相匹配的顺序应用。可以在一个过滤器操作[?, ?]
中指定多个谓词占位符,这两个谓词都必须匹配。
过滤器也可以与OR
和AND
一起使用
Filter fooOrBar = filter(where("foo").exists(true)).or(where("bar").exists(true));
Filter fooAndBar = filter(where("foo").exists(true)).and(where("bar").exists(true));
自定义谓词
Predicate booksWithIsbn = new Predicate() {@Overridepublic boolean apply(PredicateContext ctx) {return ctx.item(Map.class).containsKey("isbn");}
};
List<Map<String, Object>> books = reader.read("$.store.book[?].isbn", List.class, booksWithIsbn);
Configuration
Option
源码为com.jayway.jsonpath.Option
,枚举类:
- DEFAULT_PATH_LEAF_TO_NULL:当检索不到时返回null对象,否则如果不配置这个,会直接抛出异常PathNotFoundException
- ALWAYS_RETURN_LIST:总是返回list,即便是一个确定的非list类型,也会被包装成list
- AS_PATH_LIST:返回path
- SUPPRESS_EXCEPTIONS:不抛出异常,需要判断如下:
- ALWAYS_RETURN_LIST开启,则返回空list
- ALWAYS_RETURN_LIST关闭,则返回null
- REQUIRE_PROPERTIES:如果设置,则不允许使用通配符,比如
$[*].b
会抛出PathNotFoundException异常。
SPI
SPI是
cache
对应于源码