跳转到内容

JQuick-Path

纯 Java 8+ · Apache-2.0 · Maven Central 2.0.0

JSON 的 XPath:一条表达式,定位任意节点不手写树遍历、不层层判空——路径直达嵌套 JSON 的任何值

$.store.books..[2].price 这样一条表达式就能钻进任意深度的嵌套结构:属性访问、通配符、递归下降、数组下标与切片、[?(...)] 过滤、算术下标共 15 种语法形式自由组合;JPredicate 谓词库(eq/gt/like/regex/in/exists…)与完整过滤表达式引擎负责筛选,limit()/skip() 为递归下降设置边界防结果膨胀,as()/asList() 把结果直接映射成 Java Bean。纯 Java 无 JNI,Windows、Linux、macOS 与信创环境行为一致。

JSON 树 → 路径表达式 → 提取结果 — JQuick-Path
JSON 树 → 路径表达式 → 提取结果

左:任意形状的嵌套文档;中:字符串路径或编程式 JPath;右:原始结果或 as() 映射的 Bean

  • 15 种路径语法形式:递归、切片、过滤、算术下标
  • 18 个谓词与逻辑算子,含 regex / in / exists
  • 4 种输入来源:JSON 字符串 / Map / POJO / JSONObject
  • 0 JNI纯 Java 跨平台,信创环境已验证

生态位:JQuick 生态的 JSON 取值底座

这个引擎不只在文档里——生态组件的 JSON 提取能力就构建在它上面。

JQuick-Connector数据接入

path() 字段处理器的路径表达式由 JQuick-Path 解析:$、属性访问、数组下标、切片、通配符等写法直接可用,异构数据源按 JSON 路径取嵌套字段。

查看 Connector 集成 →
JQuick-SQL内存查询

jsonPath(json, path) 函数基于 JSONPathQueryBuilder 实现:SQL 里对行内的 JSON 字段按路径取值,与 259 个内置函数同一套 SPI 体系。

查看 SQL 集成 →
5 行最简示例:四行从嵌套 JSON 里取值
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.path("$.store.books..[2].price")
.limit(10)
.execute();
System.out.println(result.getRawData()); // 20

示例与输出取自文档快速开始章节:from → path → limit → execute,getRawData() 直接得到 20。

asList过滤 + Bean 映射:表达式筛完直接变成对象
// 过滤:等价路径表达式 $.books[?(@.price>15)]
List<Book> books = JSONPathQueryBuilder.from(jsonData)
.path("$.books[?(@.price>15)]")
.sort(JSortBuilder.desc("price"))
.asList(Book.class); // Book{title='Book 3', price=20}

同一套流式 API:sort(JSortBuilder.desc("price")) 排序,as()/asList() 映射 POJO,无需手工遍历与转型。

同样的嵌套取值,手写树遍历要付出什么

比较的是「表达与防护的成本」,不是「能不能实现」。

对比维度JQuick-Path手写 JSON 树遍历
取值表达一条路径表达式直达任意深度:递归、切片、过滤自由组合逐层 get/instanceof/判空,嵌套越深代码越长
条件筛选[?(...)] 过滤表达式 + 18 个谓词算子,筛选在路径里完成先取集合再手写循环与比较逻辑
结果落地as()/asList() 直接映射 Java Bean,sort/limit/skip 链式收尾手工遍历 Map 构造对象,转型与装配各写一遍
不可信输入递归下降受 limit()/skip() 约束,遍历有界防结果膨胀防护逻辑自己实现,容易遗漏
多态输入JSON 字符串 / Map / POJO / JSONObject 四种来源同一入口每种来源各写一套读取与转换
平台覆盖纯 Java 无 JNI,Windows / Linux / macOS / 信创行为一致取决于所选 JSON 库的本地依赖情况

不是完整的 JSON 库,也不替代序列化框架:就是专注把「从嵌套 JSON 里取值」收敛成一条有边界的路径表达式。

JQuick-Path 是一种用于从 JSON 文档中提取数据的查询语言与运行时,它在 JSON 中扮演的角色,正如 XPath 在 XML 中的角色:通过一条简洁的路径表达式,即可定位嵌套 JSON 结构中的任意节点。

JQuick-Path 采用纯 Java 实现,由自研语法解析器驱动,不包含任何本地依赖,在 Windows、Linux、macOS 以及信创操作系统上行为一致。库底层提供流式、类型安全的查询构建器 JSONPathQueryBuilder,以及完整的表达式引擎:根节点与当前节点选择器、属性访问、通配符、递归下降、数组下标、切片、过滤、算术与逻辑表达式,并支持将查询结果映射为 Java Bean。

JQuick-Path 的依赖中不包含 Spring,无需任何容器即可独立引入使用:普通 Java 项目与 Spring Boot 项目均使用同一套 API。

核心特性一览:

  • 流式查询构建器: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() 分页。
  • 语法驱动解析:自研语法规则配合专用错误监听器,语法错误提示清晰。
  • 跨平台与信创适配:纯 Java 字节码、无 JNI,已在 Windows、Linux、macOS 及麒麟、统信 UOS、openEuler、Anolis 等环境与主流国产 JDK 上验证。
  • 遍历有界:对不可信 JSON 的递归下降受 limit() / skip() 约束,可防止结果无界膨胀,构成轻量的路径遍历防护。
  • 声明式注解:@JSONPath、@JSONObject、@JSONArray、@JSONFilter、@JSONSort、@JSONFormat,用于基于路径的字段与结果绑定。
  • 可扩展工具:JValueContextFactory 提供基于上下文的取值能力,JTemplateEngine 提供模板求值能力。
  • Apache-2.0 开源协议:可自由用于商业与个人项目。

典型使用场景:

  • 配置读取:从 JSON 或由 YAML 转换而来的配置文档中取出嵌套值。
  • API 响应处理:从第三方响应中提取并重组字段。
  • 数据转换:通过 as() / asList() 将 JSON 片段直接映射为 Java Bean。
  • 批量与流水线作业:在下游处理之前,对大型 JSON 集合进行过滤、切片、排序与分页。
  • 不可信载荷检查:递归下降受 limit() / skip() 约束,路径表达式无法在攻击者可控的 JSON 上无限制膨胀。

环境要求:

项目 要求
JDK 8 或更高版本(推荐 JDK 17)
构建工具 Maven 3.6+ 或 Gradle 7+
操作系统 Windows / Linux / macOS / 信创操作系统
依赖 内置词法与语法分析运行时、Hutool、Javelin Core、JQuick JSON,均为纯 Java,无本地库
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-path</artifactId>
<version>2.0.0</version>
</dependency>

版本号请以 Maven Central 上的最新版本为准。

implementation 'io.github.paohaijiao:jquick-path:2.0.0'

README 同时提供了表达式函数支持文档:JEvaluator Function Reference。

从嵌套 JSON 中提取单个数值,输出结果为 20:

public class QuickStartDemo {
public static void main(String[] args) {
// 示例数据
String jsonData = "{\"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 -> price
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.subscript(JIndexSubscript.of(2)))
.property("price"))
.limit(10)
.execute();
// 输出:20
System.out.println(result.getRawData());
}
}

基础语法如下:

表达式 说明
$ 根对象
. 或 [ ] 子操作符,用于访问对象属性
.. 递归下降,搜索所有子元素(语法定义中写作 ...,示例中写作 ..)
* 通配符,匹配所有对象或数组元素
[ ] 下标操作符,用于数组索引或过滤
[start:end:step] 数组切片操作
?() 过滤表达式
@ 当前节点,用于过滤表达式
  • 作用:访问对象的属性。. 与 [ ] 均为子操作符。
  • 编程式对应:JPath.property(String) 追加属性段;JSegments.id(String) 生成标识符段;JSubscripts.property(String) 生成 ['title'] 形式的对象属性下标。
  • 参数:属性名称,字符串,必填。
public class FieldAccessDemo {
public static void main(String[] args) {
String jsonData = "{\"store\":{\"books\":[]}}";
// 等价路径表达式:$.store.books
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
  • 作用:通过下标定位数组元素。
  • 取值规则:数字下标从 0 开始;README 在 JSubscripts.index(int) 中说明,负数下标从数组末尾开始计数。
  • 编程式对应:JSubscripts.index(int)、JIndexSubscript.of(int)。
public class IndexDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}";
// 等价路径表达式:$.books[0]
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.index(0))))
.limit(10)
.execute();
// 输出索引为 0 的书籍对象(Book 1)
System.out.println(result.getRawData());
}
}

README 还给出了负数下标示例 $.books[-2](通过 JSubscripts.expr("-2") 指定),输出从末尾计数的对应元素。

  • 作用:通配符匹配对象的全部属性或数组的全部元素。
  • 编程式对应:JSubscripts.wildcard()、property("*")、JSegments.wildcard()。
public class WildcardDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}";
// 等价路径表达式:$.books[*]
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.wildcard())))
.limit(10)
.execute();
// 输出 books 数组中的全部书籍对象
System.out.println(result.getRawData());
}
}

可用能力如下:

  • $.*:通配符,获取根对象(或当前对象)下的全部属性,见上文示例。
  • 入口方法 JSONPathQueryBuilder.select(Class<T>):用于创建带类型的 JProjectionBuilder。

当前版本不支持在一条表达式中一次指定多个具名字段;需要按字段集合取数时,用 JSONPathQueryBuilder.select(Class<T>) 声明目标类型。

  • 作用:通过 [?(...)] 过滤表达式筛选数组元素,表达式中以 @ 表示当前节点。
  • 编程式对应:JSubscripts.filter(JPredicate<T>)。
  • 谓词:可使用 JPredicate 谓词工厂,或通过 JPredicate.custom(String) 传入原始过滤表达式。

README 记载的谓词工厂能力:

谓词 / 方法 作用
eq / ne 等于 / 不等于
gt / ge / lt / le 大于 / 大于等于 / 小于 / 小于等于
like(prop, pattern) 通配符匹配(*、?)
regex(prop, regex) 正则表达式匹配
startsWith / endsWith / contains 基于正则实现的常见字符串匹配
in(prop, values...) 成员归属判断
exists / notExists 属性是否存在
and / or / not 逻辑组合
custom(String) 原始 JSONPath 过滤表达式
of(prop, operator, value) 显式指定操作符的通用谓词
public class FilterSyntaxDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}";
// 等价路径表达式:$.books[?(@.title == 'Book 1')]
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.eq("title", "Book 1")))))
.limit(10)
.execute();
// 输出符合条件的书籍对象(Book 1)
System.out.println(result.getRawData());
}
}

综合 README 语法表与 33 个示例,路径表达式中出现过的形式如下:

形式 含义 README 示例
$ 根对象 $.store.books
@ 当前节点 @.store.books
.field 属性访问 $.store.books[2].price
['field'] 对象属性下标 $.extract['title']
[index] 数字下标 $.books[0]
[-index] 负数下标(自末尾计数) $.books[-2]
[*] 全部数组元素 $.books[*]
.* 全部对象属性 $.*
..field 递归下降匹配属性 $.store.books..price
..* 递归通配 由 JSegments.recursiveWildcard() 生成
..[index] 递归下标 $.store.books..[2]
[start:end] 切片 JSubscripts.slice(Integer, Integer)
[start:end:step] 带步长切片 $.books[0:1:2]
[?(...)] 过滤表达式 $.books[?(@.price>15)]
[expr] 算术 / 函数表达式下标 $.books[1+1]、$.books[(@.length())-1]

README 过滤表达式中出现过的运算符与写法:算术 +、-、*、/、%;比较 >、>=、<、<=、==、!=;逻辑 &&、||、!;成员判断 in ('a','b');取非;正则;以及 @.length() 嵌套函数调用。可用函数以 README 链接的 JEvaluator 函数参考为准。

临时解析:直接传入路径字符串(简单场景)

Section titled “临时解析:直接传入路径字符串(简单场景)”
  • 作用:将路径表达式字符串直接传入 path(String),无需事先构建路径对象,适合一次性、临时性的查询。
  • 参数:路径表达式字符串,必填。
  • 返回值:execute() 返回 JSONPathResult。
public class DirectPathDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}}";
// 直接使用字符串路径表达式
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.path("$.store.books..[2]")
.limit(10)
.execute();
// 输出 books 数组中索引为 2 的书籍对象(Book 3)
System.out.println(result.getRawData());
}
}

README 核心 API 参考中,入口构建器 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> 的方法如下:

方法 作用
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

JSONPathResult 的方法如下:

方法 作用 返回值
getRawData() 获取原始结果对象 Object
getAsList() 以 List 形式获取结果(单值会被包装) List
isList() 判断结果是否为列表 boolean

根符号 JRoot:

常量 符号 作用
JRoot.ROOT $ 根文档
JRoot.CURRENT @ 当前节点

下例对应 README 示例 $.extract['title'],输出 extract 对象的 title 属性值。

public class PropertyDemo {
public static void main(String[] args) {
String jsonData = "{\"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}],"
+ "\"extract\":{\"title\":\"Book 3\",\"author\":\"Author 3\",\"price\":20}}";
// 等价路径表达式:$.extract['title']
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT).property("extract")
.segment(JSubscriptSegment.of(JSubscripts.property("title"))))
.limit(10)
.execute();
// 输出:Book 3
System.out.println(result.getRawData());
}
}

下例对应 README 示例 $.store.books[2],输出数组中索引为 2 的元素(Book 3)。

public class ArrayElementDemo {
public static void main(String[] args) {
String jsonData = "{\"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)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.subscript(JIndexSubscript.of(2))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}

下例对应 README 示例 $.store.books.*,输出数组中的全部书籍对象。

public class AllElementsDemo {
public static void main(String[] args) {
String jsonData = "{\"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.*
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books").property("*"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}

条件过滤,获取符合条件的节点集合

Section titled “条件过滤,获取符合条件的节点集合”

下例对应 README 示例 $.books[?(@.price>15)],使用 JPredicate.custom(String) 传入原始过滤表达式,输出价格大于 15 的书籍对象。

public class ConditionDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}";
// 等价路径表达式:$.books[?(@.price>15)]
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.custom("@.price>15")))))
.limit(10)
.execute();
// 输出符合条件的书籍对象(Book 3)
System.out.println(result.getRawData());
}
}

下例对应 README 示例 $.store.books..price,以递归下降方式搜索 books 数组下所有的 price 属性值,输出结果为 [10, 15, 20]。

public class NestedDemo {
public static void main(String[] args) {
String jsonData = "{\"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..price
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.recursiveId("price")))
.limit(10)
.execute();
// 输出:[10, 15, 20]
System.out.println(result.getRawData());
}
}

多路径一次性批量提取多个结果

Section titled “多路径一次性批量提取多个结果”

该能力暂未在README文档中介绍。

README 记载的映射方式为流式查询上的两个终端方法:

  • as(Class<T>):将单个结果映射为 Java Bean。
  • asList(Class<T>):将列表结果映射为 Java Bean。
// 普通书籍 POJO,字段对应参考 JSON 中的属性
public class Book {
private String title;
private String author;
private Integer price;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Book{title='" + title + "', author='" + author + "', price=" + price + "}";
}
}
public class BeanMappingDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}}";
// README API:as(Class<T>) —— 将单个结果映射为 Java Bean
Book book = JSONPathQueryBuilder.from(jsonData)
.path("$.store.books[2]")
.as(Book.class);
// 输出:Book{title='Book 3', author='Author 3', price=20}
System.out.println(book);
}
}
public class BeanListMappingDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}}";
// README API:asList(Class<T>) —— 将列表结果映射为 Java Bean
List<Book> books = JSONPathQueryBuilder.from(jsonData)
.path("$.store.books[*]")
.asList(Book.class);
books.forEach(System.out::println);
}
}

README 另记载 sort(JSortBuilder<T>) 可对列表结果排序(需配合 asList),排序构建器提供 JSortBuilder.asc() / desc(),但 README 未给出排序代码示例。

字段映射别名、忽略不存在字段

Section titled “字段映射别名、忽略不存在字段”

README 特性清单中列出了 @JSONPath、@JSONObject、@JSONArray、@JSONFilter、@JSONSort、@JSONFormat 等声明式注解,但未说明字段别名映射与忽略不存在字段的配置方式。

该能力暂未在README文档中介绍。

README 明确记载支持四种输入来源:JSON 字符串、Map、POJO(先序列化再解析)、JSONObject。

public class StringSourceDemo {
public static void main(String[] args) {
String jsonString = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\"},"
+ "{\"title\":\"Book 2\"}]}}";
// 入口方法:from(String)
JSONPathResult result = JSONPathQueryBuilder.from(jsonString)
.path("$.store.books[0].title")
.limit(10)
.execute();
// 输出:Book 1
System.out.println(result.getRawData());
}
}
public class MapSourceDemo {
public static void main(String[] args) {
Map<String, Object> root = new HashMap<String, Object>();
Map<String, Object> store = new HashMap<String, Object>();
store.put("name", "JQuick");
root.put("store", store);
// 入口方法:from(Map)
JSONPathResult result = JSONPathQueryBuilder.from(root)
.path("$.store.name")
.limit(10)
.execute();
// 输出:JQuick
System.out.println(result.getRawData());
}
}

README 记载 @(JRoot.CURRENT)表示当前节点,并提供入口方法 from(JSONObject) 以 JSONObject 作为查询起点。下例对应 README「当前节点表达式」示例,路径从当前节点开始访问 store、books。

public class NodeSourceDemo {
public static void main(String[] args) {
String jsonData = "{\"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
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.CURRENT)
.property("store").property("books"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}

README 仅介绍了语法层面的错误诊断:自研语法规则配合专用错误监听器,提供清晰的语法错误提示。下列与缺失节点、默认值、异常模式、数组越界相关的运行时策略,README 均未介绍。

该能力暂未在README文档中介绍。

该能力暂未在README文档中介绍。

开启抛出异常模式(缺失节点抛异常)

Section titled “开启抛出异常模式(缺失节点抛异常)”

该能力暂未在README文档中介绍。

该能力暂未在README文档中介绍。

获取原始节点元信息(节点类型、父节点)

Section titled “获取原始节点元信息(节点类型、父节点)”

该能力暂未在README文档中介绍。

README 记载的编程式路径构建能力即用于动态组合路径。路径构建器 JPath / JPaths 的方法如下:

方法 作用
JPath.fromRoot(JRoot) 从根节点($)或当前节点(@)开始构建路径
JPath.documentRoot() / JPaths.documentPath() 从根节点开始构建路径
JPath.currentRoot() / JPaths.currentPath() 从当前节点开始构建路径
segment(JSegment) / segments(JSegment...) 追加一个或多个段
property(String) 追加属性标识符段
wildcard() 追加通配符段
index(int) 追加下标段
filter(JPredicate<?>) 追加过滤下标段
recursiveProperty(String) 追加递归标识符段
recursiveWildcard() 追加递归通配符段
toJSONPathExpression() 将路径渲染为 JSONPath 字符串

段工厂 JSegments:

方法 作用
id(String) 标识符段(.property)
wildcard() 通配符标识符段(.*)
subscript(JSubscript) 下标段([...])
recursiveId(String) 递归标识符段(..property)
recursiveWildcard() 递归通配符段(..*)
recursiveSubscript(JSubscript) 递归下标段(..[...])

下标工厂 JSubscripts:

方法 作用
index(int) 数字下标,负数自末尾计数
wildcard() 全部元素([*])
property(String) 对象属性(['title'])
slice(Integer, Integer) 起止切片([0:1])
slice(Integer, Integer, Integer) 带步长切片([0:1:2])
filter(JPredicate<T>) 过滤表达式([?(...)])
expr(String) 原始算术 / 函数表达式下标([0*1])

通过上述 API,可以在运行时按业务条件逐段拼接路径,例如 README 示例中「路径段 + 下标段 + 属性段」的组合:

public class DynamicPathDemo {
public static void main(String[] args) {
String jsonData = "{\"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}]}}";
int index = 2;
// 等价路径表达式:$.store.books[2].price
JSONPathResult result = JSONPathQueryBuilder.from(jsonData)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.subscript(JIndexSubscript.of(index)))
.property("price"))
.limit(10)
.execute();
// 输出:20
System.out.println(result.getRawData());
}
}

该能力暂未在README文档中介绍。

本节按由浅入深的学习顺序组织 7 类、共 24 个可直接复制运行的 Demo。每个 Demo 固定包含四块:等价路径表达式、输入 JSON、完整 Java 代码、执行输出。

所有 Demo 共享同一份输入数据,便于横向对比不同路径表达式的效果:

{
"store": {
"books": [
{ "title": "Book 1", "author": "Author 1", "price": 10, "isbn": true },
{ "title": "Book 2", "author": "Author 2", "price": 15, "isbn": false },
{ "title": "Book 3", "author": "Author 3", "price": 20, "isbn": true }
]
},
"books": [
{ "title": "Book 1", "author": "Author 1", "price": 10, "isbn": true },
{ "title": "Book 2", "author": "Author 2", "price": 15, "isbn": false },
{ "title": "Book 3", "author": "Author 3", "price": 20, "isbn": true }
],
"extract": { "title": "Book 3", "author": "Author 3", "price": 20 }
}

路径表达式 $.store.books:从根节点出发,依次访问 store、books 属性,得到 books 数组。

{
"store": {
"books": [
{ "title": "Book 1", "author": "Author 1", "price": 10, "isbn": true },
{ "title": "Book 2", "author": "Author 2", "price": 15, "isbn": false },
{ "title": "Book 3", "author": "Author 3", "price": 20, "isbn": true }
]
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
public class BasicRootDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\",\"author\":\"Author 1\",\"price\":10,\"isbn\":true},"
+ "{\"title\":\"Book 2\",\"author\":\"Author 2\",\"price\":15,\"isbn\":false},"
+ "{\"title\":\"Book 3\",\"author\":\"Author 3\",\"price\":20,\"isbn\":true}]}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("store").property("books"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 1","author":"Author 1","price":10,"isbn":true},{"title":"Book 2","author":"Author 2","price":15,"isbn":false},{"title":"Book 3","author":"Author 3","price":20,"isbn":true}]

路径表达式 $.*:获取根对象下的全部属性值,结果以列表形式返回。

{
"store": { "books": [] },
"books": [],
"extract": { "title": "Book 3", "author": "Author 3", "price": 20 }
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
public class BasicWildcardDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":[{\"title\":\"Book 1\",\"price\":10}]}},"
+ "\"books\":[{\"title\":\"Book 1\",\"price\":10}],"
+ "\"extract\":{\"title\":\"Book 3\",\"author\":\"Author 3\",\"price\":20}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("*"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"books":[{"title":"Book 1","price":10}]},[{"title":"Book 1","price":10}],{"title":"Book 3","author":"Author 3","price":20}]

路径表达式 $.extract['title']:使用 ['属性名'] 形式访问对象属性,等价于 $.extract.title。

{ "extract": { "title": "Book 3", "author": "Author 3", "price": 20 } }
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class BasicPropertyDemo {
public static void main(String[] args) {
String json = "{\"extract\":{\"title\":\"Book 3\",\"author\":\"Author 3\",\"price\":20}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("extract")
.segment(JSubscriptSegment.of(JSubscripts.property("title"))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
Book 3

路径表达式 $.store.books.*:通过通配符取 books 数组的全部元素。

{
"store": {
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
public class BasicArrayAllDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books").property("*"))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 1","price":10},{"title":"Book 2","price":15},{"title":"Book 3","price":20}]

路径表达式 $.books[0]:取数组下标为 0 的元素。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class IndexPositiveDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.index(0))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 1","price":10}

路径表达式 $.books[*]:取数组全部元素,与 $.books.* 等价。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class IndexWildcardDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.wildcard())))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 1","price":10},{"title":"Book 2","price":15}]

路径表达式 $.books[-2]:负数下标从数组末尾计数,-2 表示倒数第二个元素(长度为 3 时即下标 1)。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class IndexNegativeDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.expr("-2"))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 2","price":15}

路径表达式 $.books[0:1:2]:[start:end:step] 切片,从下标 0 到 1(不含),步长 2,结果只含下标 0 的元素。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class IndexSliceDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.slice(0, 1, 2))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 1","price":10}]

路径表达式 $.store.books..price:递归下降搜索 books 下所有层级的 price 属性,返回值列表。

{
"store": {
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.util.JSegments;
public class RecursivePropertyDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.recursiveId("price")))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[10,15,20]

路径表达式 $.store.books..[2]:递归下降后取下标为 2 的元素。

{
"store": {
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.subscript.JIndexSubscript;
import com.github.paohaijiao.util.JSegments;
public class RecursiveIndexDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT)
.property("store").property("books")
.segment(JSegments.recursiveSubscript(JIndexSubscript.of(2))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 3","price":20}

路径表达式 $.books[?(@.title == 'Book 1')]:过滤出 title 等于 Book 1 的元素。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.function.JPredicate;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class FilterEqDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.eq("title", "Book 1")))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 1","price":10}]

路径表达式 $.books[?(@.price>15)]:过滤出 price 大于 15 的元素。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.function.JPredicate;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class FilterGtDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.custom("@.price>15")))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 3","price":20}]

路径表达式 $.books[?(@.title in ('Book 3','Book 2'))]:过滤出 title 属于指定集合的元素。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.function.JPredicate;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class FilterInDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.custom(
"@.title in ('Book 3','Book 2')")))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 2","price":15},{"title":"Book 3","price":20}]

路径表达式 $.books[?(@.title in ('Book 3','Book 2') && @.isbn)]:&& 与逻辑,要求 title 属于指定集合且 isbn 为 true。

{
"books": [
{ "title": "Book 1", "price": 10, "isbn": true },
{ "title": "Book 2", "price": 15, "isbn": false },
{ "title": "Book 3", "price": 20, "isbn": true }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.function.JPredicate;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class FilterLogicDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10,\"isbn\":true},"
+ "{\"title\":\"Book 2\",\"price\":15,\"isbn\":false},"
+ "{\"title\":\"Book 3\",\"price\":20,\"isbn\":true}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.custom(
"@.title in ('Book 3','Book 2') && @.isbn")))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 3","price":20,"isbn":true}]

路径表达式 $.books[?([email protected])]:! 取非,过滤出 isbn 为 false 的元素。

{
"books": [
{ "title": "Book 1", "isbn": true },
{ "title": "Book 2", "isbn": false },
{ "title": "Book 3", "isbn": true }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.function.JPredicate;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class FilterNotDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"isbn\":true},"
+ "{\"title\":\"Book 2\",\"isbn\":false},"
+ "{\"title\":\"Book 3\",\"isbn\":true}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(
JSubscripts.filter(JPredicate.custom("[email protected]")))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
[{"title":"Book 2","isbn":false}]

路径表达式 $.books[1+1]:下标由算术表达式 1+1 计算得到,等价于 $.books[2]。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class ExprArithmeticDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.expr("1+1"))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 3","price":20}

路径表达式 $.books[(@.length())-1]:通过 @.length() 取当前数组长度,减 1 得到最后一个元素的下标。

{
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.enums.JRoot;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.selector.root.JPath;
import com.github.paohaijiao.selector.segment.JSubscriptSegment;
import com.github.paohaijiao.selector.subscript.JSubscripts;
public class ExprFunctionDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.document(JPath.fromRoot(JRoot.ROOT).property("books")
.segment(JSubscriptSegment.of(JSubscripts.expr("(@.length())-1"))))
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 3","price":20}

路径表达式 $.store.books..[2]:不使用编程式构建器,直接把路径字符串传给 path(String),适合临时查询。

{
"store": {
"books": [
{ "title": "Book 1", "price": 10 },
{ "title": "Book 2", "price": 15 },
{ "title": "Book 3", "price": 20 }
]
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONPathResult;
public class ExprStringPathDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"books\":["
+ "{\"title\":\"Book 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"price\":15},"
+ "{\"title\":\"Book 3\",\"price\":20}]}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.path("$.store.books..[2]")
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
{"title":"Book 3","price":20}

路径表达式 $.books[2]:查询结果为 JSONObject,通过 toBean(Class) 映射为 Java Bean。

{
"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 }
]
}
public class Book {
private String title;
private String author;
private Integer price;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public Integer getPrice() { return price; }
public void setPrice(Integer price) { this.price = price; }
@Override
public String toString() {
return "Book{title='" + title + "', author='" + author + "', price=" + price + "}";
}
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONObject;
import com.github.paohaijiao.model.JSONPathResult;
public class BeanSingleDemo {
public static void main(String[] args) {
String json = "{\"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}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.path("$.books[2]")
.execute();
// 单值结果为 JSONObject,调用 toBean 完成字段映射
Book book = ((JSONObject) result.getRawData()).toBean(Book.class);
System.out.println(book);
}
}
Book{title='Book 3', author='Author 3', price=20}

路径表达式 $.books[*]:列表结果逐项调用 toBean(Class),组装为 List<Book>。

{
"books": [
{ "title": "Book 1", "author": "Author 1", "price": 10 },
{ "title": "Book 2", "author": "Author 2", "price": 15 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONObject;
import com.github.paohaijiao.model.JSONPathResult;
import java.util.ArrayList;
import java.util.List;
public class BeanListDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 1\",\"author\":\"Author 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"author\":\"Author 2\",\"price\":15}]}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.path("$.books[*]")
.execute();
List<Book> books = new ArrayList<Book>();
for (Object item : result.getAsList()) {
books.add(((JSONObject) item).toBean(Book.class));
}
books.forEach(System.out::println);
}
}
Book{title='Book 1', author='Author 1', price=10}
Book{title='Book 2', author='Author 2', price=15}

路径表达式 $.books:asList(Class) 配合 JSortBuilder.asc("price"),在查询管道内完成排序并将结果映射为 List<Book>。

{
"books": [
{ "title": "Book 3", "author": "Author 3", "price": 20 },
{ "title": "Book 1", "author": "Author 1", "price": 10 },
{ "title": "Book 2", "author": "Author 2", "price": 15 }
]
}
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONPathResult;
import com.github.paohaijiao.query.impl.JSortBuilder;
import java.util.List;
public class BeanSortDemo {
public static void main(String[] args) {
String json = "{\"books\":["
+ "{\"title\":\"Book 3\",\"author\":\"Author 3\",\"price\":20},"
+ "{\"title\":\"Book 1\",\"author\":\"Author 1\",\"price\":10},"
+ "{\"title\":\"Book 2\",\"author\":\"Author 2\",\"price\":15}]}";
JSONPathResult result = JSONPathQueryBuilder.<Book>from(json)
.path("$.books")
.sort(new JSortBuilder<Book>().asc("price"))
.asList(Book.class)
.execute();
List<Book> books = (List<Book>) result.getRawData();
books.forEach(System.out::println);
}
}
Book{title='Book 1', author='Author 1', price=10}
Book{title='Book 2', author='Author 2', price=15}
Book{title='Book 3', author='Author 3', price=20}

路径表达式 $.store.name:输入源为 JSON 字符串,使用 from(String)。

{ "store": { "name": "JQuick Bookstore" } }
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONPathResult;
public class SourceStringDemo {
public static void main(String[] args) {
String json = "{\"store\":{\"name\":\"JQuick Bookstore\"}}";
JSONPathResult result = JSONPathQueryBuilder.from(json)
.path("$.store.name")
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
JQuick Bookstore

路径表达式 $.store.name:输入源为 Map,使用 from(Map)。

{ "store": { "name": "JQuick Bookstore" } }
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONPathResult;
import java.util.HashMap;
import java.util.Map;
public class SourceMapDemo {
public static void main(String[] args) {
Map<String, Object> store = new HashMap<String, Object>();
store.put("name", "JQuick Bookstore");
Map<String, Object> root = new HashMap<String, Object>();
root.put("store", store);
JSONPathResult result = JSONPathQueryBuilder.from(root)
.path("$.store.name")
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
JQuick Bookstore

路径表达式 $.store.books[0].title:输入源为 JSONObject,使用 from(JSONObject)。

{ "store": { "books": [ { "title": "Book 1" } ] } }
import com.github.paohaijiao.builder.JSONPathQueryBuilder;
import com.github.paohaijiao.model.JSONObject;
import com.github.paohaijiao.model.JSONPathResult;
public class SourceObjectDemo {
public static void main(String[] args) {
JSONObject book = new JSONObject();
book.put("title", "Book 1");
JSONObject store = new JSONObject();
store.put("books", java.util.Collections.singletonList(book));
JSONObject root = new JSONObject();
root.put("store", store);
JSONPathResult result = JSONPathQueryBuilder.from(root)
.path("$.store.books[0].title")
.limit(10)
.execute();
System.out.println(result.getRawData());
}
}
Book 1

README 未提供 JQuick-Path 与 JsonPath、Jackson、Gson 的直接对比内容,仅在依赖清单中列出内置词法与语法分析运行时、Hutool、Javelin Core、JQuick JSON 等纯 Java 依赖。

该能力暂未在README文档中介绍。

该能力暂未在README文档中介绍。

README 未设立「已知限制」章节。其中明确记载的一项设计约束为:

  • 遍历有界:针对不可信 JSON 的递归下降受 limit() / skip() 约束,可防止结果无界膨胀与资源耗尽。

除上述设计约束外,README 未列举其它限制项。

JQuick-Path 基于 Apache License, Version 2.0 发布。

Copyright (c) [2025-2099] Martin ([email protected])
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.