JSONPath 是一种用于从 JSON 文档中提取数据的查询语言,类似于 XPath 之于 XML。在现代 API 开发和数据处理中,JSONPath 已成为不可或缺的工具。本指南将全面介绍 JSONPath 的语法、原理和实战应用。
目录
核心要点
- 类XPath语法:JSONPath 借鉴了 XPath 的设计理念,使用路径表达式从 JSON 中提取数据。
- 灵活查询:支持通配符、递归下降、数组切片和过滤器表达式。
- 跨语言支持:几乎所有主流编程语言都有 JSONPath 实现库。
- API测试必备:在 REST API 测试和数据验证中广泛使用。
- 无需遍历:一行表达式即可替代多层嵌套循环的数据提取逻辑。
- 标准化进程:IETF 正在推进 JSONPath 的标准化(RFC 9535)。
需要快速测试 JSONPath 表达式?试试我们的免费在线工具,支持实时预览和语法高亮。
立即测试JSONPath - 免费在线JSONPath测试工具
什么是JSONPath?
JSONPath 是由 Stefan Goessner 在 2007 年提出的一种查询语言,用于从 JSON 数据结构中提取特定的值或子集。它的设计灵感来自 XPath(用于 XML 的查询语言),但针对 JSON 的数据模型进行了优化。
JSONPath 的核心思想是使用路径表达式来导航 JSON 文档的层级结构,就像文件系统路径一样直观。
JSONPath vs XPath 对比:
| 特性 | JSONPath | XPath |
|---|---|---|
| 根节点 | $ |
/ |
| 当前节点 | @ |
. |
| 子节点 | . 或 [] |
/ |
| 递归下降 | .. |
// |
| 通配符 | * |
* |
| 数组索引 | [n] |
[n+1] (1-based) |
| 过滤器 | [?(expr)] |
[expr] |
JSONPath语法详解
基础语法
让我们以一个示例 JSON 来学习 JSONPath 的基础语法:
{
"store": {
"name": "技术书店",
"books": [
{
"title": "JavaScript高级程序设计",
"author": "Nicholas C. Zakas",
"price": 99.00,
"category": "programming",
"inStock": true
},
{
"title": "深入理解计算机系统",
"author": "Randal E. Bryant",
"price": 139.00,
"category": "computer-science",
"inStock": true
},
{
"title": "算法导论",
"author": "Thomas H. Cormen",
"price": 128.00,
"category": "algorithm",
"inStock": false
}
],
"location": {
"city": "北京",
"address": "中关村大街1号"
}
}
}
基础路径表达式:
| 表达式 | 描述 | 结果 |
|---|---|---|
$ |
根对象 | 整个JSON文档 |
$.store |
store对象 | {"name": "技术书店", ...} |
$.store.name |
store的name属性 | "技术书店" |
$.store.books[0] |
第一本书 | {"title": "JavaScript高级程序设计", ...} |
$.store.books[0].title |
第一本书的标题 | "JavaScript高级程序设计" |
$.store.location.city |
城市 | "北京" |
运算符和表达式
JSONPath 提供了丰富的运算符来处理各种查询需求:
1. 通配符 *
匹配任意属性或数组元素:
$.store.books[*].title // 所有书的标题
$.store.* // store下的所有属性
2. 递归下降 ..
在整个文档中递归搜索:
$..title // 文档中所有的title
$..price // 文档中所有的price
$.store..inStock // store下所有的inStock
3. 数组切片 [start:end:step]
类似 Python 的切片语法:
$.store.books[0:2] // 前两本书(索引0和1)
$.store.books[-1:] // 最后一本书
$.store.books[::2] // 每隔一本书(索引0, 2, ...)
$.store.books[1:3] // 第2到第3本书
4. 多索引选择 [index1,index2]
选择多个特定索引:
$.store.books[0,2] // 第1本和第3本书
$.store.books[0,2].title // 第1本和第3本书的标题
5. 属性列表 ['prop1','prop2']
选择多个属性:
$.store.books[0]['title','author'] // 第一本书的标题和作者
过滤器表达式
过滤器是 JSONPath 最强大的功能之一,使用 [?()] 语法根据条件筛选数据。
基础过滤:
// 价格大于100的书
$.store.books[?(@.price > 100)]
// 有库存的书
$.store.books[?(@.inStock == true)]
// 特定分类的书
$.store.books[?(@.category == 'programming')]
复合条件:
// 价格大于100且有库存
$.store.books[?(@.price > 100 && @.inStock == true)]
// 价格小于100或分类为algorithm
$.store.books[?(@.price < 100 || @.category == 'algorithm')]
字符串匹配:
// 标题包含"JavaScript"的书
$.store.books[?(@.title =~ /JavaScript/)]
// 作者以"Thomas"开头
$.store.books[?(@.author =~ /^Thomas/)]
存在性检查:
// 有isbn属性的书
$.store.books[?(@.isbn)]
// 没有discount属性的书
$.store.books[?(!@.discount)]
想要实时测试这些表达式?使用我们的在线工具可以即时看到查询结果。
实战代码示例
JavaScript
// 使用 jsonpath-plus 库
// npm install jsonpath-plus
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
books: [
{ title: 'JavaScript高级程序设计', price: 99, inStock: true },
{ title: '深入理解计算机系统', price: 139, inStock: true },
{ title: '算法导论', price: 128, inStock: false }
]
}
};
// 获取所有书名
const titles = JSONPath({ path: '$.store.books[*].title', json: data });
console.log(titles);
// ['JavaScript高级程序设计', '深入理解计算机系统', '算法导论']
// 获取价格大于100的书
const expensiveBooks = JSONPath({
path: '$.store.books[?(@.price > 100)]',
json: data
});
console.log(expensiveBooks);
// [{ title: '深入理解计算机系统', ... }, { title: '算法导论', ... }]
// 获取有库存书籍的价格
const inStockPrices = JSONPath({
path: '$.store.books[?(@.inStock == true)].price',
json: data
});
console.log(inStockPrices);
// [99, 139]
// 使用回调函数处理结果
JSONPath({
path: '$..title',
json: data,
callback: (value, type, payload) => {
console.log(`Found title: ${value}`);
}
});
使用 jsonpath 库(更轻量):
// npm install jsonpath
import jp from 'jsonpath';
// 查询
const result = jp.query(data, '$.store.books[*].author');
// 获取路径
const paths = jp.paths(data, '$..price');
// [['$', 'store', 'books', 0, 'price'], ...]
// 获取单个值
const firstTitle = jp.value(data, '$.store.books[0].title');
// 应用到每个匹配项
jp.apply(data, '$.store.books[*].price', (value) => value * 0.9);
Python
# 使用 jsonpath-ng 库
# pip install jsonpath-ng
from jsonpath_ng import jsonpath, parse
from jsonpath_ng.ext import parse as parse_ext
data = {
"store": {
"books": [
{"title": "JavaScript高级程序设计", "price": 99, "inStock": True},
{"title": "深入理解计算机系统", "price": 139, "inStock": True},
{"title": "算法导论", "price": 128, "inStock": False}
]
}
}
# 基础查询
jsonpath_expr = parse('$.store.books[*].title')
matches = jsonpath_expr.find(data)
titles = [match.value for match in matches]
print(titles)
# ['JavaScript高级程序设计', '深入理解计算机系统', '算法导论']
# 使用扩展语法(支持过滤器)
jsonpath_expr = parse_ext('$.store.books[?@.price > 100]')
expensive_books = [match.value for match in jsonpath_expr.find(data)]
print(expensive_books)
# 获取路径信息
for match in parse('$..price').find(data):
print(f"Path: {match.full_path}, Value: {match.value}")
# 更新值
jsonpath_expr = parse('$.store.books[*].price')
jsonpath_expr.update(data, lambda x: x * 0.9) # 所有价格打9折
使用 jmespath(AWS 推荐):
# pip install jmespath
import jmespath
# JMESPath 语法略有不同但功能相似
result = jmespath.search('store.books[*].title', data)
print(result)
# 过滤
expensive = jmespath.search('store.books[?price > `100`]', data)
# 投影
titles_and_prices = jmespath.search('store.books[*].[title, price]', data)
Java
// 使用 Jayway JsonPath
// Maven: com.jayway.jsonpath:json-path:2.9.0
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.ReadContext;
import java.util.List;
import java.util.Map;
public class JsonPathExample {
public static void main(String[] args) {
String json = """
{
"store": {
"books": [
{"title": "JavaScript高级程序设计", "price": 99, "inStock": true},
{"title": "深入理解计算机系统", "price": 139, "inStock": true},
{"title": "算法导论", "price": 128, "inStock": false}
]
}
}
""";
// 简单查询
List<String> titles = JsonPath.read(json, "$.store.books[*].title");
System.out.println(titles);
// 使用ReadContext进行多次查询
ReadContext ctx = JsonPath.parse(json);
// 过滤查询
List<Map<String, Object>> expensiveBooks =
ctx.read("$.store.books[?(@.price > 100)]");
// 获取单个值
String firstTitle = ctx.read("$.store.books[0].title");
// 计算
Double avgPrice = ctx.read("$.store.books[*].price.avg()");
Integer bookCount = ctx.read("$.store.books.length()");
// 条件组合
List<String> inStockTitles =
ctx.read("$.store.books[?(@.inStock == true)].title");
System.out.println("Average price: " + avgPrice);
System.out.println("Book count: " + bookCount);
System.out.println("In stock titles: " + inStockTitles);
}
}
高级用法
1. 函数支持
部分 JSONPath 实现支持内置函数:
// 数组长度
$.store.books.length()
// 最小/最大值
$.store.books[*].price.min()
$.store.books[*].price.max()
// 平均值
$.store.books[*].price.avg()
// 求和
$.store.books[*].price.sum()
// 标准差
$.store.books[*].price.stddev()
2. 脚本表达式
某些实现允许在路径中使用脚本:
// 动态索引
$.store.books[(@.length-1)] // 最后一本书
// 条件表达式
$.store.books[?(@.price < $.maxPrice)]
3. 联合查询
组合多个查询结果:
// 获取标题和作者
$.store.books[*]['title','author']
// 获取第一本和最后一本
$.store.books[0,-1]
4. 嵌套过滤
在复杂结构中进行深层过滤:
{
"departments": [
{
"name": "Engineering",
"teams": [
{"name": "Frontend", "members": 5},
{"name": "Backend", "members": 8}
]
},
{
"name": "Design",
"teams": [
{"name": "UX", "members": 3}
]
}
]
}
// 成员数大于4的团队
$.departments[*].teams[?(@.members > 4)]
// 有超过5人团队的部门
$.departments[?(@.teams[?(@.members > 5)])]
常见使用场景
1. API响应数据提取
// 从分页API响应中提取数据
const response = await fetch('/api/users?page=1');
const data = await response.json();
// 提取用户ID列表
const userIds = JSONPath({ path: '$.data[*].id', json: data });
// 提取分页信息
const totalPages = JSONPath({ path: '$.meta.totalPages', json: data })[0];
2. 配置文件处理
# 从复杂配置中提取特定环境的设置
config = load_json('config.json')
db_config = parse('$.environments.production.database').find(config)[0].value
3. 日志分析
// 从JSON日志中筛选错误
const errorLogs = JSONPath({
path: '$[?(@.level == "error")]',
json: logs
});
// 提取特定时间范围的日志
const recentErrors = JSONPath({
path: '$[?(@.timestamp > "2026-01-01")]',
json: logs
});
4. 测试断言
// 在API测试中验证响应结构
const response = await api.get('/products');
const prices = JSONPath({ path: '$[*].price', json: response.data });
expect(prices.every(p => p > 0)).toBe(true);
expect(JSONPath({ path: '$[?(@.inStock == true)]', json: response.data }).length).toBeGreaterThan(0);
5. 数据转换
# ETL过程中的数据提取和重组
source_data = load_json('source.json')
transformed = {
'products': [match.value for match in parse('$.items[*].product').find(source_data)],
'total': parse('$.summary.total').find(source_data)[0].value
}
JSONPath最佳实践
1. 性能优化
// 避免过度使用递归下降
// 慢:$..title(搜索整个文档)
// 快:$.store.books[*].title(精确路径)
// 缓存编译后的表达式
const compiledPath = jp.parse('$.store.books[*].title');
const result1 = jp.query(data1, compiledPath);
const result2 = jp.query(data2, compiledPath);
2. 错误处理
// 始终处理空结果
const result = JSONPath({ path: '$.nonexistent.path', json: data });
if (result.length === 0) {
console.log('Path not found');
}
// 使用可选链式调用
const value = JSONPath({ path: '$.store.books[0].title', json: data })?.[0] ?? 'Default';
3. 可读性
// 使用变量存储复杂路径
const BOOK_TITLES_PATH = '$.store.books[*].title';
const EXPENSIVE_BOOKS_PATH = '$.store.books[?(@.price > 100)]';
// 添加注释说明复杂表达式
// 获取所有有库存且价格在50-150之间的编程类书籍
const PATH = '$.store.books[?(@.inStock && @.price >= 50 && @.price <= 150 && @.category == "programming")]';
4. 安全考虑
// 验证用户输入的JSONPath表达式
function safeQuery(data, userPath) {
// 禁止执行脚本
if (userPath.includes('(') && !userPath.match(/\.(length|min|max|avg)\(\)/)) {
throw new Error('Script expressions not allowed');
}
return JSONPath({ path: userPath, json: data });
}
常见问题
JSONPath和JMESPath有什么区别?
JSONPath 和 JMESPath 都是 JSON 查询语言,但有以下区别:
| 特性 | JSONPath | JMESPath |
|---|---|---|
| 根符号 | $ |
无(直接从根开始) |
| 递归下降 | .. |
不支持 |
| 过滤语法 | [?(@.field == value)] |
[?field == value] |
| 标准化 | IETF RFC 9535 | 独立规范 |
| AWS集成 | 一般 | 原生支持 |
如何处理包含特殊字符的属性名?
使用方括号语法:
// 属性名包含空格
$['store name']
// 属性名包含点号
$['config.json']
// 属性名以数字开头
$['123abc']
JSONPath表达式返回空数组怎么办?
- 检查路径是否正确
- 确认数据结构是否匹配
- 使用在线工具调试表达式
推荐使用我们的 JSONPath测试工具 进行实时调试。
不同库的JSONPath实现有差异吗?
是的,不同实现可能有细微差异:
- 过滤器语法(
@.pricevs@['price']) - 函数支持(
length(),min()等) - 正则表达式支持
- 脚本表达式支持
建议查阅具体库的文档并进行测试。
如何在不写代码的情况下测试JSONPath?
使用在线工具是最便捷的方式。我们的 JSONPath测试工具 提供:
- 实时查询预览
- 语法高亮
- 错误提示
- 常用表达式示例
总结
JSONPath 是处理 JSON 数据的强大工具,掌握它可以大大提高数据处理效率。无论是 API 开发、测试自动化还是数据分析,JSONPath 都能帮助你用简洁的表达式完成复杂的数据提取任务。
快速回顾:
- 使用
$作为根节点,.或[]访问属性 - 使用
*通配符匹配所有元素 - 使用
..进行递归搜索 - 使用
[?()]过滤器进行条件筛选 - 选择适合你项目的 JSONPath 库
准备好测试你的 JSONPath 表达式了吗?试试我们的免费在线工具: