JQuick-Path
JQuick-Path 是一套面向 JSON 文档的查询语言与运行时,它在 JSON 中扮演的角色,正如 XPath 在 XML 中的角色:一条简洁的跨平台路径表达式,就能定位嵌套结构里的任意节点。
库本身是纯 Java 实现,由 ANTLR4 语法驱动,没有本地依赖,在任何操作系统上行为一致。它同时提供流式、类型安全的构建器(JSONPathQueryBuilder)与完整的表达式引擎:根节点与当前节点选择器、属性访问、通配符、递归下降、数组下标、切片、过滤、算术与逻辑表达式,以及结果到 Java Bean 的映射。
JQuick-Path 是用于从 JSON 文档中提取数据的查询语言,作用类似于 XPath 之于 XML;它通过路径表达式以简洁的方式定位并提取 JSON 结构中的特定部分。
- 流式查询构建器:
JSONPathQueryBuilder.from(...).document(...).limit(...).execute()一气呵成。 - 编程式路径构建:用
JPath、JSegments、JSubscripts组合路径,避免手写字符串。 - 字符串路径表达式:
$.store.books..[2]之类的表达式可直接传给path(String)。 - 根节点与当前节点选择器:
$(JRoot.ROOT)与@(JRoot.CURRENT)。 - 段选择器:属性访问、通配符
*、递归下降..、下标段与递归下标段。 - 下标选择器:数字下标、负下标、通配符、对象属性
['title']、切片[start:end:step]、过滤[?(...)]与算术表达式下标。 - 丰富的谓词库:
eq、ne、gt、ge、lt、le、like、regex、startsWith、endsWith、contains、in、exists、notExists、and、or、not以及原始custom表达式。 - 完整过滤表达式引擎:比较、逻辑、算术、
in、取反、正则与@.length()之类的嵌套函数调用。 - 多种输入来源:JSON 字符串、
Map、POJO 或JSONObject。 - 结果处理:
JSONPathResult提供getRawData()、getAsList()、isList();as()/asList()直接映射为 Java Bean。 - 排序与分页:
JSortBuilder.asc()/desc()排序,limit()/skip()分页。 - 语法驱动解析:ANTLR4 语法 + 专用错误监听器,语法错误提示清晰。
- 跨平台与信创兼容:纯 Java 字节码、无 JNI,已在 Windows / Linux / macOS 与麒麟、统信 UOS、openEuler 等信创环境验证。
- 有界遍历:递归下降受
limit()/skip()约束,可防止不可信 JSON 触发无界结果膨胀。
<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-path</artifactId> <version>2.1.0</version></dependency>implementation 'io.github.paohaijiao:jquick-path:2.1.0'| 表达式 | 说明 |
|---|---|
$ |
根对象 |
. 或 [] |
子节点操作符,访问对象属性 |
.. |
递归下降,搜索所有子元素 |
* |
通配符,匹配所有对象或数组元素 |
[...] |
下标操作符,用于数组索引或过滤 |
[start:end:step] |
数组切片 |
?() |
过滤表达式 |
@ |
当前节点,用于过滤表达式内部 |
以下示例共用同一份参考数据:
{ "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 } ] }}1. 使用字符串路径表达式
Section titled “1. 使用字符串路径表达式”// 直接传入路径表达式,等价于 $.store.books..[2]JSONPathResult result = JSONPathQueryBuilder.from(jsonData) .path("$.store.books..[2]") .limit(10) .execute();
System.out.println(result.getRawData());2. 编程式构建路径
Section titled “2. 编程式构建路径”// 从根节点出发,依次访问 store、books,再取下标为 2 的元素JSONPathResult result = JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("store") .property("books") .segment(JSegments.subscript(JIndexSubscript.of(2)))) .limit(10) .execute();3. 递归下降提取属性
Section titled “3. 递归下降提取属性”// 等价于 $.store.books..price,结果为 [10, 15, 20]JSONPathResult result = JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("store") .property("books") .segment(JSegments.recursiveId("price"))) .limit(10) .execute();通配符与切片
Section titled “通配符与切片”// 数组通配符:$.books[*]JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("books") .segment(JSubscriptSegment.of(JSubscripts.wildcard()))) .limit(10) .execute();
// 切片:$.books[0:1:2]JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("books") .segment(JSubscriptSegment.of(JSubscripts.slice(0, 1, 2)))) .limit(10) .execute();过滤与表达式下标
Section titled “过滤与表达式下标”// 过滤:$.books[?(@.title == 'Book 1')]JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("books") .segment(JSubscriptSegment.of(JSubscripts.filter(JPredicate.eq("title", "Book 1"))))) .limit(10) .execute();
// 表达式下标:$.books[(@.length())-1],取最后一个元素JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("books") .segment(JSubscriptSegment.of(JSubscripts.expr("(@.length())-1")))) .limit(10) .execute();
// 原始过滤表达式:$.books[?(@.price>15 && @.isbn)]JSONPathQueryBuilder.from(jsonData) .document(JPath.fromRoot(JRoot.ROOT) .property("books") .segment(JSubscriptSegment.of( JSubscripts.filter(JPredicate.custom("@.price>15 && @.isbn"))))) .limit(10) .execute();映射为 Java Bean 并排序分页
Section titled “映射为 Java Bean 并排序分页”// 列表结果映射为 Java Bean,并支持排序与分页JSONPathQueryBuilder.from(jsonData) .path("$.store.books[*]") // 取全部书籍 .sort(JSortBuilder.<Book>desc("price")) // 按 price 倒序(asc() 为升序) .limit(2) // 取前 2 条 .skip(0) // 跳过前 0 条 .asList(Book.class) // 映射为 Bean 列表 .execute();核心 API
Section titled “核心 API”JSONPathQueryBuilder(入口)
Section titled “JSONPathQueryBuilder(入口)”| 方法 | 说明 |
|---|---|
from(Object) |
从 POJO 创建查询(先序列化再解析) |
from(String) |
从 JSON 字符串创建查询 |
from(Map) |
从 Map 创建查询 |
from(JSONObject) |
从 JSONObject 创建查询 |
root(JSubscripts) |
以根下标开始创建查询 |
filter(Class<T>) |
创建带类型的 JFilterBuilder<T> |
of(JPredicate<T>) / of(String) |
由谓词或原始表达式构建 JFilterExpression |
select(Class<T>) |
创建带类型的 JProjectionBuilder<T> |
JSONPathQuery<T>(流式查询)
Section titled “JSONPathQuery<T>(流式查询)”| 方法 | 说明 |
|---|---|
document(JPath) |
设置编程式构建的路径 |
path(String) |
设置字符串路径表达式 |
limit(int) |
限制返回元素个数 |
skip(int) |
跳过前 N 个元素(与 limit 配合使用) |
sort(JSortBuilder<T>) |
对列表结果排序(需配合 asList) |
as(Class<T>) |
单个结果映射为 Java Bean |
asList(Class<T>) |
列表结果映射为 Java Bean |
execute() |
执行查询并返回 JSONPathResult |
JPath / JSegments / JSubscripts / JPredicate
Section titled “JPath / JSegments / JSubscripts / JPredicate”| 方法 | 说明 |
|---|---|
JPath.fromRoot(JRoot) |
从根节点($)或当前节点(@)构建路径 |
property(String) / wildcard() |
追加属性段 / 通配段 |
segment(JSegment) |
追加一个段 |
JSegments.id(String) |
标识符段(.property) |
JSegments.recursiveId(String) |
递归标识符段(..property) |
JSegments.recursiveSubscript(JSubscript) |
递归下标段(..[...]) |
JSubscripts.index(int) |
数字下标,负数从末尾计算 |
JSubscripts.property(String) |
对象属性(['title']) |
JSubscripts.slice(Integer, Integer) |
起止切片([0:1]) |
JSubscripts.slice(Integer, Integer, Integer) |
起止 + 步长切片([0:1:2]) |
JSubscripts.filter(JPredicate<T>) |
过滤表达式([?(...)]) |
JSubscripts.expr(String) |
原始算术 / 函数表达式下标([0*1]) |
JPredicate.eq / ne / gt / ge / lt / le |
比较谓词 |
JPredicate.like / regex / startsWith / endsWith / contains |
字符串谓词 |
JPredicate.in(prop, values...) |
集合包含 |
JPredicate.exists / notExists |
属性存在性 |
JPredicate.and / or / not / custom |
逻辑组合与原始表达式 |
JSONPathResult
Section titled “JSONPathResult”| 方法 | 说明 |
|---|---|
getRawData() |
原始结果对象 |
getAsList() |
以 List 返回结果(单值自动包装) |
isList() |
结果是否为列表 |