




















从 Stefan Gössner 2007 年的博客文章,到 2024 年正式成为 IETF 标准,JSONPath 走过了 17 年的标准化历程。本文带你深入了解 RFC 9535 的核心特性,并用 snack4-jsonpath 实战演示。
在 JSON 统治 API 世界的今天,我们几乎每天都在处理 JSON 数据。你是否遇到过这样的场景:
传统的方案要么需要编写大量代码遍历解析,要么依赖不兼容的各种实现。RFC 9535 的出现,终于结束了这种混乱局面。
RFC 9535 是 IETF(互联网工程任务组)于 2024 年 2 月正式发布的标准规范,全称:
JSONPath: Query Expressions for JSON
即「用于 JSON 的查询表达式」
该规范由三位作者共同编写:
RFC 9535 的核心可以概括为:
JSONPath 定义了一种字符串语法,用于从给定的 JSON 值中选择和提取 JSON 值。
简单来说,JSONPath 就是 JSON 的「XPath」——用类似路径表达式的方式查询 JSON 数据。
| 特性 | JSONPath | JMESPath | JSON Pointer |
|---|---|---|---|
import org.noear.snack4.jsonpath.JsonPath;
String json = "{\"store\":{\"book\":[{\"author\":\"张三\",\"price\":8.95},{\"author\":\"李四\",\"price\":12.99}],\"bicycle\":{\"color\":\"red\",\"price\":399}}}";
import org.noear.snack4.jsonpath.JsonPath;
// 选择所有子节点
JsonPath.select(json, "$.store.*"); // [数组, 对象] - store 下的所有成员
import org.noear.snack4.jsonpath.JsonPath;
// 索引选择(从 0 开始)
JsonPath.select(json, "$.store.book[0]"); // 第一本书
JsonPath.select(json, "$.store.book[-1]");
..这是 JSONPath 最强大的特性之一:
// 递归查找所有 author 字段
JsonPath.select(json, "$..author"); // ["张三", "李四"]
// 递归查找所有 price 字段
JsonPath.select(json, "$..price");
[?(...)]RFC 9535 的过滤表达式使用 @ 代表当前节点:
// 基础比较
JsonPath.select(json, "$.store.book[?(@.price < 10)]");
// 结果:[{"author":"张三","price":8.95}]
// 字符串匹配
JsonPath.select(json, "$.store.book[?(@.author == '张三')]");
RFC 9535 定义了标准函数扩展接口,snack4-jsonpath 完整实现:
// length() - 获取长度
JsonPath.select(json, "length($.store.book)"); // 2
// count() - 计数(RFC 9535)
JsonPath.select(json, "count($.store.book)");
// match() - 正则匹配(需启用完整模式)
JsonPath.select(json, "$.store.book[?match(@.author, '张.*')]");
// search() - 搜索(包含)
JsonPath.select(json, "$.store.book[?search(@.author, '三')]");
// min() / max() / avg() / sum()
String enhancedJson = "{\"prices\":[8.95,12.99]}";
JsonPath.select(enhancedJson, "$.prices.min()");
// 比较操作符
@.price == 10 // 等于
@.price != 10
// 正则匹配
@.author =~ /张.*/
// 集合操作
@.status in ["active", "pending"]
@.age nin [10, 20]
String apiResponse = """
{
"code": 200,
"data": {
"users": [
{"id": 1, "name": "Alice", "orders": [{"amount": 100}, {"amount": 200}]},
{"id": 2, "name": "Bob", "orders": [{"amount": 150}]},
{"id": 3, "name": "Charlie", "orders": []}
]
}
}
""";
String config = """
{
"environments": {
"dev": {"host": "localhost", "port": 8080},
"staging": {"host": "staging.example.com", "port": 80},
"prod": {"host": "prod.example.com", "port": 443, "ssl": true}
},
"current": "prod"
}
""";
// 提取并验证数据
String json = """
{
"products": [
{"name": "笔记本", "price": 4999, "stock": 100},
{"name": "鼠标", "price": 99, "stock": 0}
]
}
""";
snack4-jsonpath 同时支持 RFC 9535(IETF)模式和 Jayway 模式:
| 特性 | RFC 9535 (默认) | Jayway 模式 |
|---|---|---|
import org.noear.snack4.Options;
import org.noear.snack4.Feature;
// RFC 9535 模式(默认)
JsonPath jp1 = JsonPath.parse("$.store.book[?(@.price > 10)]");
| 语法 | 说明 | 示例 |
|---|---|---|
RFC 9535 附录 B 专门讨论了 JSONPath 与 XPath 的关系。
JSONPath 从 XPath 汲取了大量灵感:
| XPath | JSONPath | 含义 |
|---|---|---|
但 JSONPath 有自己的特色:
以前:同一段 JSONPath 表达式在不同库中可能有不同的行为。
现在:遵循 RFC 9535 的实现必须产生一致的输出。
RFC 9535 配套了官方的 JSONPath Compliance Test Suite (CTS),实现者可以用它验证规范符合度。
RFC 9535 第 4 节专门讨论了安全问题:
.. 攻击snack4-jsonpath 通过以下方式应对:
// 可选的异常抑制
Options opts = new Options(Feature.JsonPath_SuppressExceptions);
String json = """
{
"users": [
{"name": "Alice", "age": 30, "city": "Beijing"},
{"name": "Bob", "age": 25, "city": "Shanghai"}
]
}
""";
// 获取归一化路径(Normalized Path)
String path = JsonPath.select(json, "$.users[0].name").getPath();
// 解析后缓存,可重复使用
JsonPath path = JsonPath.parse("$.store.$.category[*]");
RFC 9535 的发布标志着 JSONPath 进入了一个新的时代。从 2007 年的博客文章到 2024 年的 IETF 标准,这条路走了整整 17 年。
标准化的价值在于:
snack4-jsonpath 作为 RFC 9535 的 Java 实现,不仅完整支持了标准规范,还通过 Jayway 兼容模式保留了扩展功能。无论你是需要标准兼容性还是扩展能力,都能找到合适的方案。
相关资源:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。