ToolPal
光纤电缆中流动的发光数据

浏览器中的JSONPath:实际使用模式的实用参考和测试器

📷 Pixabay / Pexels

浏览器中的JSONPath:实际使用模式的实用参考和测试器

停止猜测你的JSONPath查询是否会匹配你期望的内容。一个关于过滤器、切片和递归下降运算符的实用指南 — 附带免费测试器。

D作者: Daniel Park2026年4月26日2分钟阅读

我使用API的时间长到足以记住XML是默认值、XPath是每个人都学习的查询语言的时代。JSONPath是JSON的等价物,在多年避开它之后(因为我可以直接写JavaScript,对吧?),我回心转意了。JSONPath是唯一可用语言的地方太多了 — Postman变量、AWS Step Functions、Kubernetes选择器、Datadog过滤器 — 流利使用它能节省真实的时间。

本指南是我刚开始时希望拥有的参考。我们将涵盖实际在生产中出现的语法、让我花费数小时的陷阱,以及ToolBox Hubs的JSONPath测试器如何让你在将表达式粘贴到任何重要位置之前检查它们。

简要介绍JSONPath

JSONPath是JSON文档的查询语言。你给它一个表达式如$.store.book[*].author,它返回所有匹配的值。$是根,点和方括号在结构中导航,特殊运算符让你过滤、切片和递归遍历。

如果你使用过XPath处理XML,心理模型可以直接转移。如果没有,把它想象成JavaScript中点表示法的更强大版本 — 一种可以用一行表达「价格低于10美元的所有书」或「文档中任何深度的每个作者」的版本。

每个人都使用的经典示例来自Stefan Goessner的原始文章:

{
  "store": {
    "book": [
      { "category": "fiction", "author": "Tolkien", "price": 22.99 },
      { "category": "reference", "author": "Rees", "price": 8.95 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

$.store.book[*].author返回["Tolkien", "Rees"]$..price返回[22.99, 8.95, 19.95]$.store.book[?(@.price < 10)]返回第二个book对象。这就是全部游戏。

你90%时间会用到的运算符

JSONPath有几十个运算符。其中五个涵盖了你在实践中编写的几乎所有内容。

$ — 根

每个JSONPath表达式都以$开头。它指文档的顶部。从技术上讲,你可以省略它(一些库很宽松),但显式写出会使意图明确。

.field — 子访问

标准点表示法。$.users.0.email从根走到users数组,选择索引0,并读取email。与JavaScript相同,只是0作为属性名工作。

[n] — 数组索引

数组访问的方括号表示法,在索引是动态的或字段名有特殊字符时有用。$.users[0]$.users[-1](最后一项)、$.users[0:3](从0到3的切片,不包含3)。

负索引从末尾计数。$.users[-1]是最后一个用户。$.users[-3:]给出最后三个。这并非在每个实现中都支持 — RFC 9535标准化了它,但如果你依赖它,请检查你的库。

[*] — 通配符

匹配数组中的所有项或对象中的所有值。$.users[*].email返回每个用户的电子邮件。$.config.*返回config对象中的每个值。与属性访问结合,这就是你扁平化常见结构的方式。

..field — 递归下降

最强大的运算符,也是我最常使用的运算符。$..price找到文档中任何位置的每个price字段,无论嵌套多深。当你不知道或不关心确切结构时很有用。

下降不关心路径 — 它只是找到所有匹配模式的内容。这使它非常适合临时数据探索(「此响应是否包含任何用户ID?」),如果你的数据在不同上下文中具有相同的键名,则很危险(用户的name字段和角色的name字段都将匹配$..name)。

过滤器:人们卡住的地方

过滤表达式写为[?(...)],其中内容是针对数组中每个项评估的布尔表达式。在过滤器内,@指当前项。

$.store.book[?(@.price < 10)]
$.users[?(@.role == 'admin')]
$.events[?(@.timestamp > 1700000000)]

@是让人绊倒的部分。它相当于forEach中的this或当前项。没有它,过滤器无法引用任何字段 — [?(price < 10)]不起作用,因为price未定义;引擎不知道要查看当前项内部。

比较运算符

跨库通用:==!=<><=>=

右侧可以是字面量:数字(@.age > 18)、单引号字符串(@.role == 'admin')、双引号字符串、truefalsenull

NOT通用的:相互比较两个字段(@.price > @.cost)、正则表达式匹配(@.email =~ /.*@example\.com/)、像length()match()这样的函数调用。一些库支持这些;许多不支持。ToolBox Hubs的JSONPath测试器支持针对字面值的基本比较,这涵盖了绝大多数实际查询。

布尔组合

标准实现支持&&||组合条件:

$.products[?(@.in_stock == true && @.price < 50)]
$.events[?(@.severity == 'error' || @.severity == 'critical')]

如果你发现自己在JSONPath中编写复杂的布尔逻辑,这通常是一个信号,要降到实际代码 — 你六个月后阅读表达式的未来自我会感谢你。JSONPath擅长简单选择;复杂的业务逻辑属于真正的语言。

切片:当你只需要一些项时

[start:end:step]的工作方式与Python切片完全相同。

  • [0:5] — 前5项
  • [5:] — 从索引5开始
  • [:3] — 前3项(与[0:3]相同)
  • [-3:] — 最后3项
  • [::2] — 每隔一项
  • [::-1] — 反转(并非处处支持)

切片是JSONPath中较干净的部分之一。它是Python的直接移植,行为可预测。我经常用它来处理「给我前N个结果」或「给我除第一项外的所有内容」模式。

工作中实际出现的模式

理论很好;让我们看看实际的查询。

过滤API响应

你的API返回分页结果,你想找到特定项。在你的测试框架或Postman中的JSONPath:

$.data[?(@.status == 'active')].id

返回所有活动项的ID。如果API没有「按状态过滤」参数,这比在代码中转换更快。

从webhook负载中提取值

你正在处理来自Stripe、GitHub或类似平台的webhook,你需要获取特定字段。AWS EventBridge规则、Step Functions或Logic Apps中的JSONPath:

$.detail.payload.items[*].sku

返回订单中每项的SKU。云工作流工具依赖JSONPath,因为它是声明性和可检查的,而嵌入JavaScript将是安全和可维护性的负担。

在深度嵌套数据中查找项

你不知道确切结构,但你知道某处有用户ID:

$..userId

返回文档中的每个userId字段,无论嵌套深度。在使用不熟悉的API响应或第三方集成时进行探索很有用。

验证类似模式的约束

你需要检查列表中的所有项是否都有特定字段:

$.users[?(!@.email)]

返回缺少电子邮件的用户。如果结果为空,你的数据是干净的。这种模式在CI测试脚本中工作良好,你解析JSON输出并对其结构进行断言。

陷阱(个人列表)

这些是让我花费数小时的事情。

过滤器中的@是必需的。[?(price < 10)]而不是[?(@.price < 10)]是最常见的错误。没有@,引擎不知道price指当前项上的字段。

过滤器返回匹配项,而不是你过滤的字段。 $..book[?(@.price < 10)]返回book 对象,不是价格。要只获取价格:$..book[?(@.price < 10)].price

表达式内的引号很重要。 在JSONPath字符串内使用单引号,以避免与周围的语言混淆。在JSON配置文件中,你的JSONPath是字符串值,所以JSONPath本身使用单引号:"$.users[?(@.role == 'admin')]"。混淆它们会产生解析错误。

递归下降找到影子匹配。 $..name将匹配每个name字段,包括不相关嵌套对象中的字段。如果你在同一文档中有用户name字段和产品name字段,这将返回两者。当重要时要具体。

索引0对属性'0'。 $.items[0]在数组上工作。$.items.0可能或可能不工作 — 大多数解析器拒绝它用于数组(它仅限方括号),但接受它作为对象的属性名。坚持使用方括号进行数组访问。

空结果不是错误。 如果你的JSONPath不匹配任何内容,你会得到空数组[],而不是异常。这是正确的行为,但它可以隐藏错字。如果你期望结果但没有得到,请仔细检查路径。

RFC 9535与Goessner时代的库

定义JSONPath的2007年原始文章是非正式的 — 它用示例描述语法,但没有正式指定边缘情况下的行为。不同的库实现做出了不同的选择,多年来差异累积。

2024年,RFC 9535作为官方规范发布。它标准化语法和语义,特别是围绕:

  • 过滤表达式语法
  • 函数扩展(lengthcountmatchsearchvalue)
  • 输出中的规范化路径(每个结果包括其位置)
  • 用于正则表达式匹配的I-Regexp配置文件

如果你在开始一个新项目,优先选择针对RFC 9535的库 — Node的jsonpath-rfc9535、Python端口jsonpath-rfc9535jp CLI工具。如果你在维护现有代码,你可能在Goessner时代的实现上;那没关系,只是要知道差异存在。

ToolBox Hubs的JSONPath测试器实现了在实现之间一致工作的最常见运算符。它在过滤器语法上有意保守,以避免测试在你的目标环境中无法工作的内容。

JSONPath输给jq的地方

如果我不提jq,我就在误导你。

JSONPath擅长在JSON中查找值。jq擅长查找AND转换JSON。如果你需要:

  • 将对象投影到不同形状(.users | map({id, email}))
  • 聚合值(map(.price) | add)
  • 组合多个操作
  • 处理复杂的条件逻辑

jq要好得多。JSONPath没有聚合,没有map/reduce,没有函数组合。这些不是bug — 是范围决策。JSONPath是查询语言。jq是转换语言。

正确的工具取决于你的环境。JSONPath无处不在,因为它易于嵌入(没有依赖,只是一个字符串解析器)。jq需要一个二进制文件,在浏览器上下文或受限环境中更难使用。在Postman测试或Step Function输入映射中,你只有JSONPath。在shell管道或服务器端数据转换中,jq通常更好。

实用工作流

这是我现在实际使用JSONPath的方式:

  1. JSON格式化器中打开API响应或JSON文档以使其可读。

  2. 识别我想要提取的值。通常我可以心里追踪路径:「users数组,项N,role字段,等于admin。」

  3. 打开JSONPath测试器,粘贴JSON,并写表达式。迭代直到它准确返回我想要的内容。

  4. 将工作表达式复制到它需要存在的地方 — Postman测试、AWS Step Function、Kubernetes选择器。

  5. 如果我在构建更复杂的东西(转换、聚合),我切换到jq。

这个工作流从集成中削减了「为什么我的JSONPath不匹配」调试的数小时。测试器是大多数教程跳过的缺失步骤 — 它们展示语法并假设你可以第一次尝试就正确编写。你通常不能,特别是过滤器。

更广泛JSON工作流的工具推荐

JSONPath是JSON工作流中的一个工具。将其与相关实用程序配对使整个管道更顺畅:

  • JSON格式化器 — 在查询前漂亮打印;压缩的JSON不可读
  • JSON Diff — 当JSONPath在两个响应之间返回不同结果时,对它们进行diff以查看更改了什么
  • JSON到TypeScript — 从样本生成TypeScript类型,然后使用类型编写匹配模式的JSONPath
  • JSON模式生成器 — 生成用于验证的JSON模式;与用于查询的JSONPath互补

JSONPath测试器是免费的,仅浏览器,从不将你的JSON发送到任何地方。粘贴、查询、迭代、复制。这就是循环。

结束语

JSONPath并不光鲜。它不是任何人都为学习而兴奋的语言。但花一个小时变得流利会在API调试、云工作流配置和数据探索的多年中得到回报。语法足够小,可以记住常见模式。陷阱足够可预测,可以内化。

如果你是新手:从基本模式开始($.field$..field$.array[*][?(@.field == 'value')]),并避免过滤器复杂性,直到你使用简单的东西一段时间。大多数现实世界的查询都很简单。复杂性来自通常用代码更好地解决的边缘情况。

常见问题

D

关于作者

Daniel Park

Senior frontend engineer based in Seoul. Seven years of experience building web applications at Korean SaaS companies, with a focus on developer tooling, web performance, and privacy-first architecture. Open-source contributor to the JavaScript ecosystem and founder of ToolPal.

了解更多

分享文章

XLinkedIn

相关文章