JSONPath 和 JSON Pointer 该怎么选
结论:JSON Pointer 是「唯一路径」,写法固定、能直接当 key 用;JSONPath 是「查询语言」,支持通配、递归、过滤。选错的典型症状是拿 JSONPath 去做配置读写,或者拿 Pointer 去做批量筛选。

结论:要读写或存储某一个节点的地址就用 JSON Pointer(如 /server/cors/0),要按条件筛出一批节点就用 JSONPath;需要持久化的路径永远存 Pointer,不存表达式。
看你要的是「一个点」还是「一批点」。
一句话区分
| JSON Pointer | JSONPath | |
|---|---|---|
| 本质 | 唯一路径标识 | 查询表达式 |
| 语法 | /a/b/0 |
$.a.b[0]、$..price |
| 结果 | 恰好一个节点(或不存在) | 零到多个节点 |
| 能当键存 | ✅ 字符串即身份 | ❌ 表达式,可能匹配多个 |
| 标准 | RFC 6901 | RFC 9535(2024 年才正式发布) |
JSON Pointer 长什么样
以斜杠开头,每一段是一个层级,数组用下标:
/server/host → "127.0.0.1"
/server/cors/0 → "https://a.com"
/ → 整个文档
键名里如果本身含 / 或 ~,需要转义:~0 表示 ~,~1 表示 /。比如键名 a/b 写成 /a~1b。
它的价值在于确定性和可序列化:路径本身就是字符串,可以存进数据库、写进日志、作为配置项的 key,两端算出来的结果必然一致。JSON Patch(RFC 6902)就是靠它定位要改哪个节点。
JSONPath 长什么样
以 $ 开头表示根,支持通配与过滤:
$.store.book[*].author → 所有作者
$..price → 递归找所有 price
$.store.book[?(@.price < 10)] → 价格低于 10 的书
$..book[-1:] → 最后一本书
它更像 SQL 的 WHERE,返回的是一个集合。同一个表达式在不同数据上可能匹配 0 个、1 个或 100 个节点。
选错的两种典型症状
症状一:拿 JSONPath 当配置项 key。 配置系统里存 $.server.port,某天结构变成数组,这个表达式可能匹配到多个端口,改值逻辑就不知道改哪个。这种场景必须用 Pointer。
症状二:拿 Pointer 做筛选。 想找「所有 price 小于 10 的节点」,Pointer 做不到——它只能按固定路径走,不知道哪些节点存在。硬写循环遍历,等于自己实现一个残缺的 JSONPath。
实际项目里的分工
大多数成熟实现是两者并存:
- 读配置、改字段、做 Patch:用 Pointer,路径固定,可审计
- 数据探索、批量提取、报表取数:用 JSONPath,表达力优先
- 需要存储的路径:一律用 Pointer,别存表达式
JSON Patch 与 Merge Patch 也是同理:Patch 用 Pointer 列表逐条操作,Merge Patch 用整体结构覆盖,前者精确、后者省事但不表达「删除数组元素」。
常见问题
JSON Pointer 和 JSONPath 应该怎么选?
看你要的是「一个点」还是「一批点」。要精确读写某个节点、并且希望这个路径能当键存起来,用 JSON Pointer,写法唯一,例如 /server/cors/0。要按条件捞出一批节点,用 JSONPath,例如 $.store.book[?(@.price < 10)]。
为什么不能把 JSONPath 表达式当配置项的 key?
因为表达式可能匹配多个节点。配置系统里存 $.server.port,某天结构变成数组,这个表达式就会匹配到多个端口,改值逻辑无法判断该改哪一个。需要持久化、可审计的定位必须用 Pointer。
能否用 JSON Pointer 做条件筛选?
不能。Pointer 只能按固定路径走,不知道哪些节点存在,所以「找出所有 price 小于 10 的节点」这类筛选它做不到。硬写循环遍历,等于自己实现一个残缺的 JSONPath。
相关工具
- JSONPath 查询:在线跑表达式,实时看匹配结果
- JSONPath 查询语言实战:通配、递归、过滤的完整语法
- JSON Pointer、Patch 与 Merge Patch:精确改值的写法