JSONPath 查询

写表达式取字段,支持通配、切片与过滤,结果回显在原 JSON 路径上。

查询引擎是手写的 JSONPath 子集解析器,先把表达式拆成词法记号(也就是页面底部「逐段解释」折叠表里的每一行),再按 RFC 9535 的语义逐段对文档求值。页面是三栏:左 JSON 文档、中命中视图、右命中列表,命中列表按具体路径逐条列出并计数,中间那栏给命中的值标底色。

只实现子集:语法是 $、.name 与 ['name'] 子节点选择器、[*] 通配、[0] 与 [-1] 下标、切片、..name 后代递归;过滤器 [?(@.age > 18)] 支持 ==、!=、>、>=、<、<=、=~(正则,属于 Jayway 风格的扩展,RFC 9535 里对应的是 match 函数)、&&、|| 和存在性测试 [?(@.a)]。刻意不支持的语法会直接报错,绝不静默返回空结果:算术与脚本表达式([?(@.a + 1) > 2])、.length 这类函数调用、@.length 之外未经引号包裹的键名歧义都在拒绝之列,报错信息会写明该写成了什么。默认最多返回 2000 条并在超出时明示截断,文档超过约 40 万字符时中间那栏拒绝渲染。

JSONPath 查询怎么用

  • 输入与预设:左栏粘 JSON 文档,中间那栏上方的一行写表达式:行首的 $ 界面已经给出,所以直接从 . 或 [ 写起就行(例如 .store.book[*].author),写成 $ 开头同样接受。下方 8 个预设按钮可以直接体验后代递归、过滤器、切片等典型写法,点一下即代入,再改成自己的查询。
  • 读结果:右栏命中列表每行是「序号 + 完整路径 + 值」,路径统一用 $.a[0].b 形式回写(键名不合法时回写成 ['name'],指向同一节点),值超过 200 字符做截断预览。中间那栏只有切到「高亮」时才给命中的值标底色,「原始」态是按缩进重排的同一份文档、不标命中。同一节点被多条路径命中时按路径去重,默认最多返回 2000 条并在超出时明示截断;文档超过约 40 万字符时中间那栏会拒绝渲染并让你先裁剪,命中列表与逐段解释照常计算。
  • 调试表达式:页面底部的「逐段解释」折叠表把表达式拆成若干段并标注每段语义与它对应的写法;查询返回 0 条时先看这张表,确认解析结果和你想要的是不是同一个东西。文档还没解析成功或表达式读不过时,这张表会直接说明它现在为什么没内容。
  • 切片、存在性与比较:[1:3]、[:5:]、[::2] 切片会在负数下标和负步长处按规范归一化。存在性看的是「节点列表是否非空」,所以值为 false 或 null 的字段照样命中——这是 RFC 的口径,和很多语言实现把 false 当假值不同。比较运算只在两侧同型时有定义:数字对数字、字符串对字符串,跨类型比大小一律不匹配,而不是强行按语言惯用的隐式转换。

常见问题

为什么 ..name 查大文档有点慢?

.. 是后代递归,要对文档里每一个节点(含数组每个元素)都试一次 name 选择器,复杂度是 O(节点数);再叠加过滤器就会对每个候选节点求值一次表达式。数据流是浏览器的 JSON.parse,解析本身通常比查询更快。缩小范围(先 $..orders 再在其下精确取键)是最有效的优化。

为什么服务端返回的路径和这里不一样?

常见原因有三个:服务端实现用 Goessner 旧语义(比如把 false 当不匹配、跨类型比较按隐式转换走),或支持算术表达式而这里明确拒绝;路径格式也可能不同($['a']['b'] 与 $.a.b 指向同一节点,本工具在键名合法时用点号简写);还有的是服务端不保证顺序,本工具按文档遍历顺序输出。结果对不上时,先看「逐段解释」确认双方解析的是同一棵树。

RFC 9535 和旧写法到底差在哪?

RFC 9535 把语法和求值语义写成规范:切片下标归一化、比较仅限同型、存在性只看节点列表非空、通配与去重都有定义。Goessner 2007 的原始实现是 JavaScript 上的一次性 hack,把路径拼成脚本求值,于是 .length、@ 参与算术都"碰巧"能用;Jayway JsonPath 等实现各自加了函数和扩展。本工具按 RFC 口径实现,=~ 是为实用加的例外,其余扩展语法宁可报错也不给近似答案。

对应功能编号 #103无需安装,手机端同样可用按 Ctrl + K 换工具