JSONPath 是 JSON 的查询语言,就像 XPath 是 XML 的一样。它 由 Stefan Goessner 在 2007 年起草,每个需要它的人都以不同方式实现, 最终在 2024 年标准化为 RFC 9535。这就是为什么两个 JSONPath 实现经常不一致,以及为什么带有结果的速查表比 符号列表更有用的原因。
下面的每个查询都在此文档上运行。你可以将其粘贴到 JSONPath 测试器中,它将其作为示例加载。
{
"store": {
"book": [
{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 },
{ "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 },
{ "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }
],
"bicycle": { "color": "red", "price": 19.95 }
},
"expensive": 10
}
选择器
根。 $ 是整个文档。每个查询从那里开始。
按名称子级。 $.store.bicycle.color 返回 "red"。一个点后跟名称,
根据需要多次使用。
引号名称。 $['store']['bicycle'] 是用另一种方式书写的相同内容。
当键包含空格、点或破折号时,或者
当它以数字开头时,需要使用括号: $['first name']。
通配符。 $.store.bicycle.* 返回 "red" 和 19.95,该对象的
每个子级。$.store.book[*] 返回所有三本书。
索引。 $.store.book[0] 是第一本书。$.store.book[-1] 是
最后一本,从末尾倒数。
切片。 $.store.book[0:2] 返回前两本书。末尾是
排他的,就像在 Python 中一样。$.store.book[::2] 每隔一本取一本,
负步长会反转: $.store.book[::-1]。
联合。 $.store.book[0, 2] 返回第一和第三本书。
$.store.bicycle['color', 'price'] 同时选择两个命名子级。
后代。 $..price 在任何深度返回每个价格,这里是
8.95、12.99、8.99 和 19.95。这是人们最常用的
选择器,也是在大型文档上开销最大的选择器,因为它访问每个值。
过滤器。 $.store.book[?@.price < 10] 返回两本较便宜的书。@
指代被测试的值。RFC 9535 删除了旧教程中使用的
括号,因此 [?@.price < 10] 和 [?(@.price < 10)] 在实践中都
出现。此处的测试器接受两者。
存在性。 ./store.book[?@.isbn] 仅返回具有 isbn
字段的书,而不与任何内容进行比较。
组合测试。 &&、|| 和 ! 如你所料地工作:
$..book[?@.category == 'fiction' && @.price < 10].title 返回
"Moby Dick"。
为什么看起来正确的过滤器返回空
几乎总是,这是类型不匹配。规范说不同类型
的值永远不相等也永远不排序,因此存储为 "8.95" 的带引号价格
不匹配 @.price < 10,也不匹配 @.price == 8.95。
如果过滤器返回空,在检查查询之前先检查文档中的类型。
第二个原因是将过滤器附加到错误的层级。过滤器从
它所附加的值的子级中选择,因此它放在数组后面:
book[?@.price < 10],而不是 book.price[?...]。附加到对象时,它测试
该对象的每个属性值。
第三,缺失值不是 null。@.isbn == null 匹配其
isbn 字面为 null 的书,而不是根本没有 isbn 的书。要测试
缺失,写 !@.isbn。
实现不一致的地方
2007 年的文章是提案而不是规范,因此库在十七年间 分歧。如果你在工具之间移动查询,这些是需要 检查的地方:
- 负索引和负步长现在广泛支持,一些较旧的库中 缺失。
- 带括号的过滤器形式是旧的拼写。RFC 9535 使用裸
[?...],许多工具只接受其中一个。 - RFC 定义了
length()、count()、match()和search()函数。 许多实现缺少这些函数或在不同名称下提供不同的集合。 - RFC 给结果一个文档顺序。一些实现不保证 这一点,特别是对于后代段。
本站的测试器实现上面列出的选择器并清楚说明, 包括其支持内容的表格。函数扩展、 过滤器内的绝对路径以及比较两个路径 未实现,使用它们会产生命名该功能的消息, 而不是语法错误。这是一个深思熟虑的选择: 声称完全 合规然后在边缘情况下静默失败的工具比告诉你 它的边缘在哪里的工具更差。
JSONPath 或 JSON Pointer
它们回答不同的问题。
JSON Pointer 命名恰好一个值并解析或
不解析。它没有通配符和过滤器,这正是为什么 JSON
Schema、JSON Patch 和大多数验证错误消息使用它的原因: 无歧义
地址正是它们所需要的。/store/book/0/title 表示一个地方。
JSONPath 查询是一种搜索。它可以返回多个值或没有值,并且可以 过滤。当你事先不知道你想要的东西在哪里时使用它。
如果你知道地址,使用指针。如果你在寻找东西,使用 查询。本站上的两个工具都报告每个结果所在的行,因此 无论如何你最终回到文档而不是抽象结果列表中。