概述
JsonPath 类似于 XML 中的 XPath,主要用于路径检索或设置 JSON 数据。它支持两种语法风格:点符号(dot-notation)和括号符号(bracket-notation),例如 $.store.book[0].title 或 $['store']['book'][0]['title']。
基础操作符
| 符号 | 描述 |
|---|---|
| $ | 查询的根节点对象,表示整个 JSON 数据(数组或对象) |
| @ | 过滤器断言处理的当前节点对象,类似 Java 中的 this |
| * | 通配符,匹配任意名称或数字 |
| .. | 递归搜索(Deep scan),在任何需要名称的地方可用 |
| . | 表示一个子节点 |
| ['' (, '')] | 表示一个或多个子节点 |
| [ (, )] | 表示一个或多个数组下标 |
| [start:end] | 数组片段切片,区间为 [start, end),不包含 end |
| [?()] | 过滤器表达式,结果必须为 boolean |
内置函数
这些函数可在 JsonPath 表达式执行后调用,输入值为表达式的结果。
| 名称 | 描述 | 输出类型 |
|---|---|---|
| min() | 获取数值类型数组的最小值 | Double |
| max() | 获取数值类型数组的最大值 | Double |
| avg() | 获取数值类型数组的平均值 | Double |
| stddev() | 获取数值类型数组的标准差 | Double |
| length() | 获取数值类型数组的长度 | Integer |
过滤逻辑
过滤器是用于筛选数组的逻辑表达式。典型形式如 [?(@.age > 18)],可以通过逻辑运算符 && 或 || 组合多个条件。字符串需用单引号或双引号包围,例如 [?(@.color == 'blue')]。
| 操作符 | 描述 |
|---|---|
| == | 等于(注意数字 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": "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 或 $..author | 所有书籍的作者列表 |
$.store.* | 显示 store 下所有叶子节点值 |
$..price | 递归查找所有价格字段 |
$..book[0,1] 或 $..book[:2] | 前两本书 |
$..book[-2:] | 最后两本书 |
$..book[2:] | 从索引 2 开始的书 |
$..book[?(@.isbn)] | 所有拥有 isbn 属性的书 |
$.store.book[?(@.price < 10)] | 价格低于 10 的书 |
$..book[?(@.price <= $['expensive'])] | 价格低于 expensive 字段的书 |
$..book[?(@.author =~ /.*REES/i)] | 作者名符合正则的书 |
$..* | 返回所有节点 |
$..book.length() | 书籍数量 |
常用 API 模式
通常直接使用静态方法 API 进行调用:
String json = "...";
List<String> authors = JsonPath.read(json, "$.store.book[*].author");
这种方式适合单次解析。如果需要对同一个 JSON 解析多次,不建议重复调用 read,因为每次都会重新解析。此时建议使用 ReadContext 或 WriteContext:
String json = "...";
ReadContext ctx = JsonPath.parse(json);
List<String> authorsOfBooksWithISBN = ctx.read("$.store.book[?(@.isbn)].author");
// 使用配置构建器
List<Map<String, Object>> expensiveBooks = JsonPath
.using(configuration)
.parse(json)
.read("$.store.book[?(@.price > 10)]", List.class);
返回值与类型转换
read 后的返回值会自动转型到指定类型。对于明确定义的表达式,应指定对应类型;对于含糊表达式(包含 ..、?[...] 等),通常应使用数组接收。如需转换成具体类型,可通过 configuration 配置 mappingProvider:
String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class); // 使用 JsonSmartMappingProvider
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class); // 使用 GsonMappingProvider
自定义映射提供者
JsonPath 支持 SPI 扩展反序列化器。默认使用 JsonSmartMappingProvider,它注册了基本数据类型的转换逻辑。若需切换 Provider(例如使用 Jackson),可配置如下:
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
@Override public JsonProvider jsonProvider() { return jsonProvider; }
@Override public MappingProvider mappingProvider() { return mappingProvider; }
@Override public Set<Option> options() { return EnumSet.noneOf(Option.class); }
});
断言谓词实现
创建过滤器断言有三种方式:
1. Inline Predicates
直接在表达式中使用 ?[<@expression>]:
List<Map<String, Object>> books = JsonPath.parse(json)
.read("$.store.book[?(@.price < 10)]");
2. Filter API
使用 Filter 类构建复杂逻辑:
import static com.jayway.jsonpath.JsonPath.parse;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
Filter cheapFictionFilter = filter(
where("category").is("fiction").and("price").lte(10D)
);
List<Map<String, Object>> books = parse(json).read("$.store.book[?]", cheapFictionFilter);
// 支持 or 和 and 组合
Filter fooOrBar = filter(where("foo").exists(true)).or(where("bar").exists(true));
注意:表达式中必须有占位符 ?,多个占位符会按顺序替换。
3. 自定义 Predicate
实现 Predicate 接口处理特殊逻辑:
Predicate booksWithISBN = new Predicate() {
@Override
public boolean apply(PredicateContext ctx) {
return ctx.item(Map.class).containsKey("isbn");
}
};
List<Map<String, Object>> books = reader.read("$.store.book[?].isbn", List.class, booksWithISBN);
获取路径列表
有时需要获取匹配到的完整路径而非值,可使用 AS_PATH_LIST 选项:
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST)
.build();
List<String> pathList = using(conf).parse(json).read("$..author");
// 结果示例:
// "$['store']['book'][0]['author']"
// "$['store']['book'][1]['author']"
配置选项详解
DEFAULT_PATH_LEAF_TO_NULL
检索不到时返回 null 而不是抛出异常。默认行为是抛出 PathNotFoundException。
Configuration conf2 = conf.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']"); // 返回 null
ALWAYS_RETURN_LIST
总是返回 List,即使结果是单个非 List 类型也会被包装。
SUPPRESS_EXCEPTIONS
不抛出异常。开启 ALWAYS_RETURN_LIST 时返回空 List,否则返回 null。
AS_PATH_LIST
返回路径列表(见上文)。
REQUIRE_PROPERTIES
禁止使用通配符,否则抛出异常。
缓存机制
每次 read 都会尝试获取缓存以提高性能,但默认未启用。JsonPath 2.1.0+ 提供了新的 SPI 缓存实现:
NOOPCache: 无缓存LRUCache: 默认实现,线程安全
如需自定义缓存:
CacheProvider.setCache(new Cache() {
private Map<String, JsonPath> map = new HashMap<>();
@Override public JsonPath get(String key) { return map.get(key); }
@Override public void put(String key, JsonPath jsonPath) { map.put(key, jsonPath); }
});


