Java JSONPath 语法不会用?jquick-path 四大核心语法实战:根对象、属性访问、递归下降、通配符一次讲透
还在为复杂 JSON 一层层取值、反复判空而烦恼?本文从实际开发角度出发,带你掌握 jquick-path 最常用的四类核心语法,并分别给出 path 路径表达式和 Java 便捷访问方式。
Git 地址
https://github.com/paohaijiao/jquick-path
Maven 坐标
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-path</artifactId>
<version>最新版本</version>
</dependency>
一、为什么需要 jquick-path?
在 Java 后端开发中,JSON 几乎无处不在:
- 微服务接口之间使用 JSON 传输数据;
- 第三方 API 返回 JSON;
- 配置中心和规则系统依赖 JSON;
- 数据转换和接口聚合需要频繁提取 JSON 字段。
最初大家通常会直接手写取值逻辑,但随着 JSON 层级变深,代码很容易变成这样:
- 一层层访问对象;
- 一层层判断是否为空;
- 遇到数组还要手动遍历;
- 相同查询逻辑散落在多个业务类中。
这类代码短期可以运行,长期却会带来可读性差、重复代码多和维护成本高等问题。
jquick-path 的目标,就是把 JSON 数据访问抽象成更清晰的路径语法,让开发者能够用统一方式完成 JSON 查询。
本文只聚焦四个最常用、最值得掌握的核心语法:
- 根对象;
- 访问对象属性;
- 递归下降;
- 通配符。
掌握这四类语法,就能覆盖大量日常 JSON 数据提取场景。
二、准备一份示例 JSON
本文统一使用下面这份数据进行说明:
{
"store": {
"books": [
{
"title": "Book 1",
"author": "Author 1",
"price": 10
},
{
"title": "Book 2",
"author": "Author 2",
"price": 15
},
{
"title": "Book 3",
"author": "Author 3",
"price": 20
}
]
}
}
这份数据具备几个典型特点:
- 顶层存在
store对象; store下存在books数组;- 数组中的每个元素都是图书对象;
- 图书对象包含
title、author、price属性。
接下来所有案例都围绕这份数据展开。
三、先看四大语法速查表
| 语法 | path 示例 | 作用 |
|---|---|---|
| 根对象 | $.store |
从 JSON 根节点开始查询 |
| 访问对象属性 | $.store.books |
逐层访问对象属性 |
| 递归下降 | $.store.books..price |
向下递归查找所有 price 属性 |
| 通配符 | $.store.books.* |
获取 books 数组中的全部元素 |
| 通配符属性 | $.* |
获取根对象下的所有属性 |
可以把这四类语法简单理解为:
- 根对象:从哪里开始;
- 对象属性:沿哪条路径走;
- 递归下降:不确定层级时向下搜索;
- 通配符:不指定具体名称或索引,批量匹配。
四、根对象语法:从 JSON 的起点开始查询
4.1 根对象是什么?
根对象表示整个 JSON 文档的起点。
在 path 表达式中,通常使用 $ 表示根节点。所有从完整 JSON 文档开始的查询,都可以从 $ 出发。
例如,查询顶层的 store 对象:
$.store
查询 store 下的 books 数组:
$.store.books
4.2 根据 path 路径查询
使用 path 方式时,可以直接把路径表达式传给查询构建器:
JSONPathQueryBuilder.from(jsonObject)
.path("$.store.books")
.limit(10)
.execute();
这段代码表达的意思非常明确:
- 从 JSON 根对象开始;
- 进入
store属性; - 继续进入
books属性; - 执行查询并限制返回数量。
4.3 使用 Java 便捷方式查询根对象
如果你更喜欢在 Java 中显式构建路径,可以使用 JRoot.ROOT:
JSONPathQueryBuilder.from(jsonObject)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books"))
.limit(10)
.execute();
这里的 JRoot.ROOT 就表示从根对象开始,后面的 property 表示沿属性继续访问。
4.4 根对象语法适合什么场景?
根对象适合绝大多数固定结构 JSON 查询,例如:
- 读取接口返回中的固定字段;
- 读取配置文件中的指定节点;
- 从完整 JSON 文档中获取业务数据;
- 作为后续通配符和递归查询的起点。
从程序员角度看,根对象语法的价值在于:让查询入口保持明确。
五、访问对象属性:沿着 JSON 路径逐层取值
5.1 属性访问的基本写法
访问对象属性,是 JSONPath 使用频率最高的语法。
在 jquick-path 中,可以使用点号表达层级关系:
$.store.books
它表示:
$:根对象;store:进入根对象的store属性;books:进入store对象的books属性。
如果继续访问数组元素中的字段,路径还可以继续向后扩展:
$.store.books.title
在实际数组查询中,也可以配合下标或其他选择器访问具体元素。
5.2 根据 path 访问对象属性
JSONPathQueryBuilder.from(jsonObject)
.path("$.store.books")
.limit(10)
.execute();
如果要访问指定图书的价格,可以使用类似下面的路径:
$.store.books[2].price
对应 Java 调用:
JSONPathQueryBuilder.from(jsonObject)
.path("$.store.books[2].price")
.limit(10)
.execute();
这里的访问顺序是:
- 从根对象进入
store; - 进入
books数组; - 获取索引为
2的图书; - 读取该图书的
price属性。
5.3 使用 Java 便捷方式访问对象属性
使用 Java 链式方式时,可以连续调用 property:
JSONPathQueryBuilder.from(jsonObject)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books"))
.limit(10)
.execute();
如果需要继续访问某个数组元素的属性,可以在路径中追加对应的 segment:
JSONPathQueryBuilder.from(jsonObject)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books")
.segment(JSegments.subscript(JIndexSubscript.of(2)))
.property("price"))
.limit(10)
.execute();
这种写法的优势是路径结构在 Java 代码中非常清楚,适合:
- 需要封装成公共方法的查询逻辑;
- 需要和业务代码组合的场景;
- 不希望在代码中到处散落完整字符串路径的场景。
5.4 点号和属性访问的工程价值
对象属性访问看起来简单,但它解决的是非常核心的工程问题:
- 让 JSON 层级关系可以直接阅读;
- 让数据访问逻辑从业务流程中独立出来;
- 让复杂 JSON 的查询路径具备统一表达方式;
- 让后续的通配符和递归语法可以自然组合。
可以说,根对象确定入口,属性访问负责搭建基本路径。
六、递归下降:不知道层级时,如何向下搜索?
6.1 递归下降解决什么问题?
在真实项目里,JSON 结构不一定永远规整。
你可能遇到这些情况:
- 相同字段出现在不同层级;
- 第三方接口版本变化导致嵌套层级不固定;
- 只知道目标字段名,不想手动写完整路径;
- 需要从一棵 JSON 对象树中搜索所有匹配字段。
如果使用传统写法,就需要自己递归遍历对象和数组。
递归下降语法就是为这类场景准备的。
6.2 根据 path 递归查找属性
在 path 表达式中,可以使用 .. 表示递归向下查找。
例如,从 store.books 下递归寻找所有 price 属性:
$.store.books..price
对应的 Java path 调用方式:
JSONPathQueryBuilder.from(jsonData)
.path("$.store.books..price")
.limit(10)
.execute();
这个表达式的重点不是固定某一层,而是:
- 先定位到
store.books; - 然后向下递归;
- 找到所有名称为
price的属性。
在示例数据中,最终可以得到所有图书的价格:
[10, 15, 20]
6.3 使用 Java 便捷方式递归查找属性
Java 方式可以使用 JSegments.recursiveId:
JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books")
.segment(JSegments.recursiveId("price")))
.limit(10)
.execute();
recursiveId("price") 表示递归寻找名为 price 的属性。
6.4 递归下降的使用建议
递归查询虽然方便,但也应该根据实际场景使用:
- 目标字段名称明确时,递归查询更合适;
- JSON 层级稳定时,优先使用明确路径;
- 数据结构复杂或来源不统一时,递归下降能明显减少代码量;
- 递归范围过大时,要注意查询数据量和执行成本。
从程序员角度看,递归下降最大的价值是:把手写递归遍历逻辑交给路径表达式处理。
七、通配符:不指定具体字段,批量匹配数据
7.1 通配符是什么?
通配符使用 * 表示。
它的含义是:不指定具体的属性名称或数组元素位置,而是匹配当前范围内的全部对象属性或数组元素。
通配符常用于两类场景:
- 匹配一个对象下的所有属性;
- 匹配一个数组中的所有元素。
7.2 根据 path 获取对象的全部属性
获取根对象下的所有属性:
$.*
对应 Java path 调用:
JSONPathQueryBuilder.from(jsonData)
.path("$.*")
.limit(10)
.execute();
也可以继续从指定对象开始使用通配符:
$.store.*
它表示获取 store 对象下的所有属性。
7.3 使用 Java 便捷方式获取对象的全部属性
Java 便捷方式中,可以把 * 作为属性名称传给 property:
JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("*"))
.limit(10)
.execute();
这就是 Java 写法和 path 写法的对应关系:
| path 写法 | Java 便捷写法 |
|---|---|
$.* |
.property("*") |
$.store.* |
.property("store").property("*") |
7.4 根据 path 获取数组全部元素
对于 books 数组,如果希望获取全部图书,可以使用:
$.store.books.*
对应 Java path 调用:
JSONPathQueryBuilder.from(jsonData)
.path("$.store.books.*")
.limit(10)
.execute();
7.5 使用 Java 便捷方式获取数组全部元素
JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books")
.property("*"))
.limit(10)
.execute();
这个写法表示:
- 进入根对象的
store; - 进入
books数组; - 使用通配符匹配数组中的所有元素。
7.6 通配符适合哪些业务场景?
通配符适合:
- 批量读取列表数据;
- 对同一类对象做统一转换;
- 不关心数组具体索引的场景;
- 动态结构 JSON 的快速扫描;
- 配置和规则系统中的范围查询。
不过也要注意,通配符会扩大匹配范围。数据规模较大时,建议结合明确的前置路径和 limit 使用。
八、四类语法如何组合使用?
实际开发中,四类语法通常不是单独使用,而是组合起来使用。
例如:
$.store.books..price
这里同时使用了:
$:根对象;.store.books:对象属性访问;..price:递归下降查找属性。
再比如:
$.store.books.*
这里组合了:
$:根对象;.store.books:属性访问;*:通配符获取全部数组元素。
这说明 jquick-path 的语法设计是组合式的。程序员不需要为每个新场景单独记忆一套 API,而是可以根据 JSON 结构逐步拼接查询路径。
九、path 和 Java 便捷方式怎么选?
9.1 优先使用 path 的场景
如果查询规则需要:
- 放入配置文件;
- 动态传入;
- 由规则系统管理;
- 快速表达和修改;
建议优先选择 path 方式。
例如:
JSONPathQueryBuilder.from(jsonData)
.path("$.store.books..price")
.limit(10)
.execute();
9.2 优先使用 Java 便捷方式的场景
如果查询规则:
- 固定写在 Java 代码中;
- 需要封装成公共查询方法;
- 需要和业务逻辑一起维护;
- 需要显式体现每个路径节点;
可以使用 Java 链式方式。
JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store")
.property("books")
.segment(JSegments.recursiveId("price")))
.limit(10)
.execute();
两种方式本质上表达的是同一类查询逻辑,选择哪种取决于代码所在的工程场景。
十、四大语法速查表
| 语法场景 | path 写法 | Java 便捷方式核心写法 |
|---|---|---|
| 根对象 | $.store.books |
JPath.fromRoot(JRoot.ROOT).property("store").property("books") |
| 访问对象属性 | $.store.books |
.property("store").property("books") |
| 递归下降属性 | $.store.books..price |
.segment(JSegments.recursiveId("price")) |
| 根对象通配符 | $.* |
.property("*") |
| 数组通配符 | $.store.books.* |
.property("store").property("books").property("*") |
十一、常见问题
1. 根对象和当前节点有什么区别?
根对象表示从完整 JSON 文档开始查询;当前节点表示从当前上下文开始查询。
固定结构数据通常从根对象开始,嵌套处理和局部查询可以考虑当前节点。
2. 什么时候应该使用递归下降?
当目标字段的层级不固定,或者你只知道字段名、不想手写完整路径时,可以使用递归下降。
3. 通配符会不会返回太多数据?
有可能。通配符会扩大匹配范围,所以建议先用明确路径缩小范围,并结合 limit 控制返回数量。
4. path 和 Java 便捷方式哪个更好?
没有绝对答案:
- 动态规则、配置化场景适合 path;
- 固定逻辑、Java 工程封装适合便捷构建方式。
十二、总结
jquick-path 的四大核心语法,可以用一句话概括:
- 根对象解决“从哪里开始查”;
- 访问对象属性解决“沿着哪条路径走”;
- 递归下降解决“层级不确定时怎么找”;
- 通配符解决“如何批量匹配一组数据”。
这四类语法看起来基础,但它们覆盖了大量真实的 Java JSON 查询场景。
更重要的是,jquick-path 同时提供 path 路径表达式和 Java 便捷访问方式,既适合配置化、规则化使用,也适合在 Java 业务代码中进行封装和复用。
如果你正在寻找:
- Java JSONPath 查询方案;
- 轻量级 JSON 数据提取工具;
- Java JSON 路径表达式库;
- 支持递归、通配符和属性访问的开源项目;
那么 jquick-path 值得加入你的技术选型清单。
更多完整语法和案例,可以参考项目 [README.md]。
关键词
Java JSONPath、jquick-path、JSONPath 语法、Java JSON 查询、JSON 数据提取、JSONPath 根对象、JSONPath 属性访问、JSONPath 递归下降、JSONPath 通配符、Java 开源项目、CSDN 技术文章