← 返回博客首页

JSONPath 实战:从复杂 JSON 中精确定位与取值

一个真实的提取诉求

后端返回了一个嵌套很深的订单响应,你想立刻取出『状态为 paid 的所有订单号及其总金额』:

{
  "orders": [
    { "id": "o1001", "status": "paid",     "total": 128.5 },
    { "id": "o1002", "status": "pending",  "total": 59.0  },
    { "id": "o1003", "status": "paid",     "items": [
        { "sku": "A-01", "price": 39.9 },
        { "sku": "A-02", "price": 20.1 }
      ], "total": 60.0 }
  ]
}

用代码,你得写 for + 判断 + 收集,至少几行;用 JSONPath,一行:

$.orders[?(@.status == "paid")]. {id, total}

这不是标准单条路径可表达的——它需要遍历 + 过滤 + 投影。JSONPath 的设计目标就是让你用一行字符串完成这套操作,尤其适合接口调试、日志清洗和配置提取。

语法速查表

表达式 含义 返回
$ 根节点 整个文档
.key / ['key'] 子属性 该属性
* 通配符 所有子属性/元素
..key 递归下降 任意层级匹配的 key
[0] 数组索引 指定元素
[0,2] 多索引 第 0、2 个元素
[start:end] 切片 区间元素
[?(@.k==v)] 过滤 满足条件的元素

逐个拆解示例

$                      → 整个对象
$.orders[1].id         → "o1002"
$.orders[*].id         → ["o1001","o1002","o1003"]
$.orders[?(@.total>60)].id
                       → ["o1001","o1003"]
$-orders..price        → [39.9,20.1]        (递归取所有 price)

注意 ..price 把嵌套在 items 里的 price 也递归摘了出来——这正是递归下降的价值。

关键概念:$@

  • $ 永远从根开始,$.a.b.c 是明确路径;
  • @ 只在过滤器里表示『当前元素』;
  • 两者混用可写『先按条件筛子集,再在子集内取某个字段』。

进阶:表达式与逻辑

$.orders[?(@.status=="paid" && @.total>=60)].id
$..[?(@.status=="paid")]                 # 任意层级筛 paid

用布尔逻辑组合条件,甚至可以对整个文档递归筛——很多工具站里的 JSONPath 查找器正是靠这套让你把筛选写进一行。

匹配到空怎么办

查询语法本身不校验『键不存在』,常见两种情况:

  1. 返回 []:路径合法但没有命中(如过滤条件无匹配对象);
  2. 抛异常:路径写法本身非法(如某段访问了数字下标但没有数组)。

判断一条 JSONPath 是否正确,先格式化 JSON(保证结构可读),再逐步拆路径段,逐层用通配符验证哪一级路径断了。这正是「先 json 格式化、再 JSONPath 查找」的组合使用场景。

实战工作流

在调试一个第三方 API 时,我的取数顺序通常是:

  1. 先格式化响应,看整体层级(避免在压缩 JSON 里数括号);
  2. $[*]. 之类粗通配确认字段名拼写;
  3. 再落到精确路径或过滤表达式取值;
  4. 反查:拿同样路径去较 JSON 差异,确认两条响应是否一致。

自查

把开头的 orders 例子丢进任意 JSONPath 工具:

  • $.orders[*].id 应返回 3 个 id;
  • $.orders[?(@.status=="paid")].id 应返回 2 个(o1001、o1003);
  • $.orders..price 应把嵌套的 39.9、20.1 也取出来。

三句全对,说明你对通配、递归、过滤的直觉已经建立。

常见问题

JSONPath 和 JSON Pointer 有什么区别?

JSONPath 更像『SQL/正则』式的**查询语言**:支持通配符(`*`)、递归下降(`..`)、数组索引、过滤表达式(`[?(@.age>18)]`),一次能返回匹配的多个节点集合,适合在复杂 JSON 中筛选数据。JSON Pointer 是**严格定位标记**,形如 `/a/b/0`,只能精确指向单个既定节点,无通配与过滤,主要用于 JSON Patch 或错误定位。简单说:要『查』用 JSONPath,要『指』用 Pointer。

`..` 递归下降到底是做什么的?

`..name` 表示『深度优先遍历整棵 JSON,把 **任意层级** 中出现名为 name 的键都取出来』,不考虑它在第几层。它适合『我不在乎字段嵌套深浅,只想把所有 price 或 id 摘出来』的场景。副作用是返回顺序按遍历序、可能跨对象无序,且在大 JSON 上比定向路径慢。精确才知道层级时,优先写完整路径(`$.a.b.c`);不确定层级时才用递归下降。

过滤表达式 `[?(@...)]` 的 `@` 是什么?

在过滤表达式内部,`@` 代表**当前正在遍历的数组元素**。`$.users[?(@.age > 30)].name` 会对 users 数组每个元素执行 `@` 等于该元素,把满足 `@.age>30` 的元素筛出来,再取它们的 name。`?` 是过滤器开始标记,`(...)` 里是布尔条件,可搭配比较符、逻辑符(`&&`/`||`)、存在性(`@.field`)。这是 JSONPath 与简单路径提取的本职差距——能在取数前先『筛』。

← 返回博客首页