JSONPath 查询指南:像专家一样提取数据
JSONPath 是 JSON 的查询语言,类似于 XPath 之于 XML。当你处理来自 API 的复杂、深层嵌套的 JSON 响应时,JSONPath 让你无需编写循环和条件语句即可提取所需的数据。本指南涵盖了你需要的语法、运算符和实际模式。
为什么用 JSONPath?
考虑一个典型的嵌套数据 API 响应:
{
"store": {
"books": [
{ "title": "Clean Code", "author": "Robert Martin", "price": 32.99, "tags": ["programming", "best-practices"] },
{ "title": "Design Patterns", "author": "Gang of Four", "price": 44.99, "tags": ["programming", "architecture"] },
{ "title": "The Pragmatic Programmer", "author": "Hunt & Thomas", "price": 39.99, "tags": ["programming", "career"] }
],
"music": [
{ "title": "Kind of Blue", "artist": "Miles Davis", "price": 12.99 }
]
}
}
要获取所有书名,你可以写一个循环——或使用 JSONPath:$.store.books[*].title
基本语法
| 表达式 | 描述 |
|---|---|
$ | 根对象 |
. | 子运算符 |
.. | 递归下降(搜索所有层级) |
[*] | 通配符(所有元素) |
[n] | 数组索引(从 0 开始) |
[n,m] | 多个索引 |
[start:end:step] | 数组切片 |
[?()] | 过滤表达式 |
@ | 当前元素(在过滤器中) |
点号表示法 vs 括号表示法
两种表示法都可以访问属性,但特殊字符需要使用括号表示法:
# 点号表示法
$.store.books[0].title
# 括号表示法(等效)
$['store']['books'][0]['title']
# 带特殊字符的键必须使用括号
$['store']['price-range']
数组操作
索引
$.store.books[0] # 第一本书
$.store.books[-1] # 最后一本书
$.store.books[0,2] # 第一和第三本书
切片
$.store.books[0:2] # 前两本书(索引 0 和 1)
$.store.books[1:] # 除第一本外的所有书
$.store.books[:2] # 前两本书
$.store.books[::2] # 每隔一本书
通配符
$.store.books[*].title # 所有书名
$.store.* # 所有商店集合(books、music)
$..title # 任何深度的所有标题
$..price # 任何深度的所有价格
递归下降运算符(..)特别强大,可以提取不论层级位置的值。
过滤表达式
过滤器根据条件选择元素:
# 低于 $40 的书
$.store.books[?(@.price < 40)]
# 特定作者的书
$.store.books[?(@.author == 'Robert Martin')]
# 标签超过 2 个的书
$.store.books[?(@.tags.length > 2)]
# 有 'price' 属性的书
$.store.books[?(@.price)]
组合过滤器
# 低于 $40 且编程标签的书
$.store.books[?(@.price < 40 && @.tags[0] == 'programming')]
实际示例
从 API 响应中提取
GitHub API — 获取所有仓库名称:
$[*].name
天气 API — 获取今天的温度:
$.daily[0].temp.day
电商 API — 获取所有产品图片:
$.products[*].images[0].url
配置提取
Docker Compose — 获取所有服务名称:
$.services.*~
Package.json — 获取所有依赖名称:
$.dependencies.*~
JSONPath vs jq
JSONPath 和 jq 都是 JSON 查询工具,但服务于不同场景:
| 特性 | JSONPath | jq |
|---|---|---|
| 环境 | 库、API | 命令行 |
| 语法 | 受 XPath 启发 | 自定义函数式 |
| 转换 | 仅查询 | 查询 + 转换 |
| 标准 | RFC 9535 | 事实标准 |
命令行 JSON 处理用 jq 更强大。在应用中嵌入查询或在 Web 工具中使用时,JSONPath 支持更广泛。
使用我们的 JSON Path 浏览器 交互式尝试 JSONPath 表达式。粘贴你的 JSON,编写查询,即时查看结果。
实现示例
JavaScript (jsonpath-plus)
const { JSONPath } = require('jsonpath-plus');
const result = JSONPath({
path: '$.store.books[?(@.price < 40)].title',
json: data
});
// ["Clean Code", "The Pragmatic Programmer"]
Python (jsonpath-ng)
from jsonpath_ng import parse
expr = parse('$.store.books[*].title')
titles = [match.value for match in expr.find(data)]
RFC 9535:JSONPath 标准
2024 年 2 月,JSONPath 正式标准化为 RFC 9535。这解决了不同实现之间的历史不一致问题。标准化的关键行为:
- 数组索引从 0 开始
- 过滤表达式使用
@表示当前元素 - 字符串比较区分大小写
- 结果始终是匹配值的数组
选择 JSONPath 库时,优先选择支持 RFC 9535 的以获得一致的行为。
常见问题
JSONPath 和 JSON Pointer 有什么区别?
JSONPath 是一种查询语言,可以使用通配符和过滤器匹配多个值。JSON Pointer(RFC 6901)是一种简单的路径语法,精确寻址一个值:/store/books/0/title。需要搜索或过滤时使用 JSONPath;知道确切路径时使用 JSON Pointer。
JSONPath 可以修改 JSON 数据吗?
标准 JSONPath 是只读的——它提取但不修改数据。一些库扩展了 JSONPath 的设置/删除操作,但这些不是标准的。对于转换,考虑使用 jq(命令行)或编写应用代码。要查看和探索 JSON 结构,我们的 JSON 格式化工具 帮助你在编写查询前理解数据。
相关资源
- JSON Path 浏览器 — 在浏览器中测试 JSONPath 表达式
- JSON 格式化最佳实践 — 结构化你的 JSON 以便更容易查询
- JSON Schema 验证指南 — 验证你的 JSONPath 查询所期望的结构