跳转到内容

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>
表达式 说明
$ 根对象
. 或 [] 子节点操作符,访问对象属性
.. 递归下降,搜索所有子元素
* 通配符,匹配所有对象或数组元素
[...] 下标操作符,用于数组索引或过滤
[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 }
]
}
}
// 直接传入路径表达式,等价于 $.store.books..[2]
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.path("$.store.books..[2]")
.limit(10)
.execute();
System.out.println(result.getRawData());
// 从根节点出发,依次访问 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();
// 等价于 $.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();
// 数组通配符:$.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();
// 过滤:$.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,并支持排序与分页
JSONPathQueryBuilder.from(jsonData)
.path("$.store.books[*]") // 取全部书籍
.sort(JSortBuilder.<Book>desc("price")) // 按 price 倒序(asc() 为升序)
.limit(2) // 取前 2 条
.skip(0) // 跳过前 0 条
.asList(Book.class) // 映射为 Bean 列表
.execute();
方法 说明
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>
方法 说明
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 逻辑组合与原始表达式
方法 说明
getRawData() 原始结果对象
getAsList() 以 List 返回结果(单值自动包装)
isList() 结果是否为列表