
本文详解如何利用 jsonata 的上下文变量(特别是 $$)对嵌套数组执行按字段分组、数值聚合及结果结构重塑,解决常见分组求和失败问题。
在 API 集成或数据管道中,常需将原始 JSON 响应按某字段(如 enterpriseid)分组,并对数值字段(如 bookingvalue)求和,最终输出结构化的新格式。JSONata 是轻量高效的选择,但其上下文切换机制易被忽略——尤其在嵌套操作(如 $distinct 内调用 $filter)中,若不显式回溯根节点,$filter 将在错误上下文中执行,导致聚合失效。
以下为正确实现方案:
✅ 正确 JSONata 查询(带注释)
{
"output": $distinct(response.{
"enterpriseid": enterpriseid,
"enterprise": enterprise,
"bookingvalue": $sum(
$filter($$.response, function($item) {
$item.enterpriseid = enterpriseid
}).bookingvalue
)
})
}? 关键点解析:
- $$.response:$$ 指向原始输入 JSON 的根对象,确保 $filter 总是遍历完整 response 数组,而非当前迭代项的子属性;
- $distinct(...):基于对象字面量中的 enterpriseid 和 enterprise 自动去重(因同 ID 企业名称一致),天然实现分组;
- $sum(...) + $filter(...):对每个唯一 enterpriseid,筛选出所有匹配项并累加 bookingvalue(支持小数,如 46 + 275 + 199.5 = 520.5)。
⚠️ 常见错误与规避
- ❌ 错误写法:$filter(response, ...) —— 此时 response 在 $distinct 内部被解析为当前迭代对象(非数组),导致过滤失败;
- ✅ 正确写法:$filter($$.response, ...) —— 强制回到根路径,获取完整数组;
- ? 提示:当嵌套多层(如 data.items.response)时,始终用 $$ 显式指定源路径,避免隐式上下文歧义。
? 验证与调试建议
- 在 JSONata Exerciser 中粘贴原始 JSON 与上述查询,实时查看结果;
- 分步调试:先测试 $$.response.enterpriseid 确认根访问正常,再逐步组合 $filter 和 $sum;
- 若企业名称存在空格/换行(如示例中 " Absolute F and B..."),聚合逻辑不受影响,但导出至下游系统前建议用 $trim(enterprise) 清理。
通过精准控制上下文,JSONata 不仅能完成基础投影,更能胜任分组聚合、条件映射等 ETL 核心任务——无需编写 JavaScript,一行声明式表达式即可生成生产就绪的数据结构。










