跳转到主要内容
CuteJSON
↑↓ 导航 Enter 选择 Esc 关闭

JSONPath 速查表,附带实际示例

发布 1 分钟阅读 作者 Akash Kinkar Pandey

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 查询是一种搜索。它可以返回多个值或没有值,并且可以 过滤。当你事先不知道你想要的东西在哪里时使用它。

如果你知道地址,使用指针。如果你在寻找东西,使用 查询。本站上的两个工具都报告每个结果所在的行,因此 无论如何你最终回到文档而不是抽象结果列表中。

返回所有文章