JSONPath Tutorial: Query JSON Like a Pro

Master JSONPath syntax with practical examples. Learn every operator, filter expression, and real-world query pattern to extract data from complex JSON structures.

JSONTech 团队February 1, 202511 min read

什么是 JSONPath?

JSONPath 是一种用于 JSON 的查询语言,类似于 XPath 对 XML 的作用。它允许您深入复杂的 JSON 结构,使用简洁的表达式语法提取所需的数据。您无需编写循环和条件语句来遍历嵌套的对象和数组,而是可以编写一个单一的路径表达式,例如 $.store.book[?(@.price < 10)].title,并获取所有价格低于十美元的书名。

Stefan Goessner 于 2007 年引入了 JSONPath,随后它成为了跨语言和工具的事实标准。如果您处理 REST API、配置文件或任何非平凡的 JSON 数据,JSONPath 将为您节省大量时间。

JSONPath 与 XPath:快速比较

如果您已经了解 XPath,JSONPath 将会让您感到熟悉。关键区别在于 JSON 具有更简单的数据模型——对象和数组,而不是元素、属性和命名空间。以下是两者的比较:

概念XPathJSONPath
/$
/child.child['child']
递归下降//..
通配符**
数组索引[1] (1 基于)[0] (0 基于)
过滤[predicate][?(expression)]

完整的 JSONPath 操作符参考

每个 JSONPath 表达式都从根 $ 开始,并链接操作符以深入导航。以下是您需要了解的所有操作符:

操作符描述示例
$根对象或数组$ — 整个文档
.key按名称访问子成员(点表示法)$.store.name
['key']按名称访问子成员(括号表示法)$['store']['name']
..递归下降 — 搜索所有后代$..price — 每个 "price" 字段
*通配符 — 对象或数组的所有成员$.store.*
[n]数组索引(0 基于)$.items[0]
[start:end]数组切片(结束是排除的)$.items[0:3] — 前三个项目
[start:end:step]带步长的数组切片$.items[::2] — 每隔一个项目
[n,m]多个数组索引$.items[0,2,4]
[?()]过滤表达式$.items[?(@.price > 10)]
@当前节点(在过滤器内部使用)@.name == 'Alice'

示例 JSON 用于我们的示例

我们将在整个教程中使用此 JSON 文档。它代表一个小型书店 API 的响应:

{
  "store": {
    "name": "TechBooks Online",
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95,
        "inStock": true
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99,
        "inStock": false
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99,
        "inStock": true
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99,
        "inStock": true
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

实用的 JSONPath 示例

1. 获取商店名称

$.store.name

结果:

"TechBooks Online"

2. 获取所有书名

$.store.book[*].title

结果:

[
  "Sayings of the Century",
  "Sword of Honour",
  "Moby Dick",
  "The Lord of the Rings"
]

3. 获取第一本书

$.store.book[0]

结果:整个第一本书对象。

4. 获取最后一本书

$.store.book[-1]

负索引从末尾计数。-1 给您最后一个元素。

5. 切片:前两本书

$.store.book[0:2]

返回索引 0 和 1 的书。结束索引是排除的,就像在 Python 和 JavaScript 的 slice() 中一样。

6. 获取整个文档中的所有价格

$..price

结果:

[8.95, 12.99, 8.99, 22.99, 19.95]

递归下降操作符 .. 找到每个 price 字段,无论深度如何——包括自行车的价格。

7. 过滤:价格低于 $10 的书

$.store.book[?(@.price < 10)]

返回价格为 8.95 和 8.99 的两本书。@ 符号指当前正在评估的项。

8. 过滤:有 ISBN 的书

$.store.book[?(@.isbn)]

仅返回《白鲸》和《指环王》。

9. 过滤:有库存的小说书籍

$.store.book[?(@.category == 'fiction' && @.inStock == true)]

您可以使用 &&(与)和 ||(或)组合条件。

10. 多个索引

$.store.book[0,3]

选择特定元素。返回第一本和第四本书。

11. 对象上的通配符

$.store.bicycle.*

结果:

["red", 19.95]

返回自行车对象的所有值。

12. 获取昂贵书籍的作者

$.store.book[?(@.price > 15)].author

结果:

["J. R. R. Tolkien"]

自己试试: 将上面的示例 JSON 粘贴到我们的 JSONPath 测试器 中进行实验。

过滤表达式深入解析

过滤表达式是 JSONPath 中最强大的部分。它们位于 [?()] 内部,并为每个元素评估布尔条件。以下是您可以使用的操作符:

操作符意义示例
==等于@.status == 'active'
!=不等于@.role != 'admin'
>大于@.age > 18
>=大于或等于@.score >= 90
<小于@.price < 50
<=小于或等于@.quantity <= 0
=~正则匹配(某些实现)@.name =~ /^J.*/
&&逻辑与@.price > 5 && @.price < 20
``

一个常见的模式是检查属性的存在性。写 @.isbn 而不带比较操作符,如果属性存在且不为 null,则返回 true

现实世界场景

从 API 响应中提取所有电子邮件

假设您从用户管理 API 收到一个响应,其中包含深度嵌套的联系对象:

{
  "users": [
    {
      "name": "Alice",
      "contacts": { "email": "alice@example.com", "phone": "555-0101" }
    },
    {
      "name": "Bob",
      "contacts": { "email": "bob@example.com", "phone": "555-0102" }
    }
  ]
}

查询:$..email

结果:

["alice@example.com", "bob@example.com"]

递归下降操作符找到每个 email 字段,无论它在层次结构中的位置。

按价格范围查找产品

$.products[?(@.price >= 25 && @.price <= 100)]

在渲染产品列表之前,客户端过滤 API 响应时非常有用。

获取嵌套配置值

$.config.database.connections[0].host

深入配置文件,而无需编写链式可选访问,如 config?.database?.connections?.[0]?.host

不同语言中的 JSONPath

JavaScript / Node.js

jsonpath-plus 包是最流行的实现:

import { JSONPath } from "jsonpath-plus";

const data = { store: { book: [{ title: "Moby Dick", price: 8.99 }] } };
const titles = JSONPath({ path: "$.store.book[*].title", json: data });
console.log(titles); // ["Moby Dick"]

Python

使用 jsonpath-ng 进行符合标准的实现:

from jsonpath_ng.ext import parse

data = {"store": {"book": [{"title": "Moby Dick", "price": 8.99}]}}
expr = parse("$.store.book[*].title")
matches = [match.value for match in expr.find(data)]
print(matches)  # ['Moby Dick']

Java

Jayway JSONPath 库是 JVM 项目的首选:

import com.jayway.jsonpath.JsonPath;

String json = "{\"store\":{\"book\":[{\"title\":\"Moby Dick\",\"price\":8.99}]}}";
List<String> titles = JsonPath.read(json, "$.store.book[*].title");
System.out.println(titles); // [Moby Dick]

注意事项和限制

JSONPath 功能强大,但在您在生产环境中依赖它之前,有一些事项需要注意:

  • 没有标准规范(直到最近)。 JSONPath 最初是非正式定义的,不同的库以微妙的差异实现它。IETF 于 2024 年发布了 RFC 9535,以标准化语法,但许多库仍遵循原始的 Goessner 规范。始终检查您库的文档以了解支持的功能。
  • 正则表达式支持各异。 =~ 正则表达式操作符并非普遍支持。Jayway Java 库支持它;许多 JavaScript 库则不支持。
  • 没有写操作。 JSONPath 是只读的。您无法使用它修改原始文档。对于变更,您需要 JSON Patch(RFC 6902)或手动遍历。
  • 在大型文档上的性能。 递归下降(..)扫描树中的每个节点。在包含数百万个节点的文档上,这可能会很慢。当性能重要时,优先选择特定路径。
  • 返回类型不一致。 一些库对单个匹配返回单个值,对多个匹配返回数组。其他库始终返回数组。请在代码中规范返回类型,以避免意外。
  • 键中的特殊字符。 如果您的 JSON 键包含点、空格或其他特殊字符,请使用括号表示法:$('my.key') 而不是 $.my.key

JSONPath 与 jq

您可能想知道 JSONPath 与流行的命令行 JSON 处理器 jq 的比较。简而言之:它们解决类似的问题,但面向不同的受众。

  • JSONPath 旨在嵌入应用程序代码中。它在每种主要语言中都有库支持,并且查询语法简单明了。
  • jq 是一个完整的数据转换语言。它可以过滤、映射、归约和重塑 JSON。它更强大,但学习曲线更陡峭,主要用于命令行。

对于在应用程序内部查询数据,JSONPath 是务实的选择。对于 shell 脚本和数据管道,jq 难以超越。

快速参考备忘单

您想要的内容JSONPath
整个文档$
特定属性$.user.name
数组中的所有项目$.items[*]
第一个项目$.items[0]
最后一个项目$.items[-1]
项目的范围$.items[1:4]
每隔 n 个项目$.items[::2]
所有 "name" 字段$..name
按值过滤$.items[?(@.price < 10)]
按存在性过滤$.items[?(@.discount)]
多个条件$.items[?(@.qty > 0 && @.active)]

自己试试: 打开 JSONPath 测试器,实时运行查询以处理您自己的 JSON 数据。

相关工具