本教程详细介绍了如何使用 Jolt 对 json 对象中的数值进行求和,并将结果添加为一个新的字段。文章通过分步解析 Jolt 转换规范,演示了如何利用 shift 操作重塑数据结构,并结合 modify-overwrite-beta 操作中的 intSum 函数实现数值累加,最终得到所需的数据输出。
问题描述
在处理 JSON 数据时,我们经常需要对某个对象中的所有数值进行汇总,并将汇总结果作为新的字段添加到输出中。例如,给定以下输入 JSON:
{ "accounts": { "canara": 1, "sbi": 0, "axis": 1, "hdfc": 0 } }
我们的目标是计算 accounts 对象中所有账户的数值之和(即 1 + 0 + 1 + 0 = 2),并将结果以 “total accounts”: 2 的形式添加到输出中,同时保留原始的账户信息。期望的输出如下:
{ "canara": 1, "sbi": 0, "axis": 1, "hdfc": 0, "total accounts": 2 }
接下来,我们将详细介绍如何使用 Jolt 来实现这一转换。
Jolt 转换实现
为了实现上述目标,我们需要设计一个包含三个操作步骤的 Jolt 转换规范:
步骤一:数据准备与扁平化 (shift 操作)
第一个 shift 操作的目的是将 accounts 对象中的所有键值对提升到根级别,并同时将所有账户的数值收集到一个临时数组中,以便后续进行求和。
{ "operation": "shift", "spec": { "accounts": { "*": [ ".&", "accountsAccumulator" ] } } }
解析:
- “accounts”:匹配输入 JSON 中的 accounts 字段。
- “*”:匹配 accounts 对象下的所有键(如 canara, sbi 等)。
- [“.&”, “accountsAccumulator”]:这是一个数组,表示对每个匹配到的键值对执行两种映射:
- “.&”:将当前匹配到的值(例如 1)映射到当前匹配到的键名(例如 canara)。这会将 {“canara”: 1}、{“sbi”: 0} 等直接提升到输出的根级别。
- “accountsAccumulator”:将当前匹配到的值(例如 1)收集到一个名为 accountsAccumulator 的数组中。由于 Jolt 默认会将相同路径的多次映射聚合为数组,所以最终 accountsAccumulator 将包含 [1, 0, 1, 0]。
经过此步骤后,中间输出将类似于:
{ "canara": 1, "sbi": 0, "axis": 1, "hdfc": 0, "accountsAccumulator": [1, 0, 1, 0] }
步骤二:执行求和计算 (modify-overwrite-beta 操作)
第二个操作是 modify-overwrite-beta,它用于执行数据计算和修改。我们将利用其内置的 intSum 函数来计算 accountsAccumulator 数组的总和。
{ "operation": "modify-overwrite-beta", "spec": { "total accounts": "=intSum(@(1,accountsAccumulator))" } }
解析:
- “total accounts”:指定要创建的新字段名称。
- “=intSum(@(1,accountsAccumulator))”:
- =:表示这是一个函数调用。
- intSum(…):Jolt 内置的整数求和函数。
- @(1,accountsAccumulator):这是一个相对路径表达式。
- @:表示引用当前操作输入中的值。
- 1:表示向上回溯一级(从当前 modify 操作的上下文,即根级别)。
- accountsAccumulator:指定要引用的字段名。 因此,@(1,accountsAccumulator) 引用的是上一步生成的 accountsAccumulator 数组 [1, 0, 1, 0]。intSum 函数将对这个数组进行求和,结果为 2。
经过此步骤后,中间输出将变为:
{ "canara": 1, "sbi": 0, "axis": 1, "hdfc": 0, "accountsAccumulator": [1, 0, 1, 0], "total accounts": 2 }
步骤三:结果整理与输出 (shift 操作)
最后一个 shift 操作的目的是清理临时字段 accountsAccumulator,并确保最终输出的结构符合要求。
{ "operation": "shift", "spec": { "accountsAccumulator": NULL, "*": "&" } }
解析:
- “accountsAccumulator”: null:将 accountsAccumulator 字段映射到 null,这在 shift 操作中等同于删除该字段,因为它不会被映射到输出中。
- “*”: “&”:这是一个通配符映射。它表示将输入中所有其他字段(除了已经被显式处理的 accountsAccumulator)按原样复制到输出中。& 表示将当前匹配到的键名作为输出的键名,其对应的值作为输出的值。这确保了 canara、sbi、axis、hdfc 和 total accounts 等字段被保留并输出。
最终,我们将得到预期的输出:
{ "canara": 1, "sbi": 0, "axis": 1, "hdfc": 0, "total accounts": 2 }
完整 Jolt 转换规范
将上述三个操作合并,完整的 Jolt 转换规范如下:
[ { "operation": "shift", "spec": { "accounts": { "*": [ ".&", "accountsAccumulator" ] } } }, { "operation": "modify-overwrite-beta", "spec": { "total accounts": "=intSum(@(1,accountsAccumulator))" } }, { "operation": "shift", "spec": { "accountsAccumulator": null, "*": "&" } } ]
核心概念与注意事项
- 多阶段转换链: 复杂的 Jolt 转换通常需要通过链式组合多个操作来完成。每个操作的输出作为下一个操作的输入,逐步实现数据转换目标。本例中,shift 用于重塑和准备数据,modify-overwrite-beta 用于计算,最后的 shift 用于清理和最终结构化。
- shift 操作的灵活性:
- & 和 .:在 shift 操作中,& 代表当前匹配的键名,. 代表当前匹配的值。它们是构建输出路径和值的重要工具。
- 数组输出:当多个输入路径映射到相同的输出路径时,Jolt 会自动将这些值聚合为一个 JSON 数组。这在收集数据(如本例中的 accountsAccumulator)时非常有用。
- modify-overwrite-beta 与数学函数:
- modify-overwrite-beta 操作是 Jolt 中执行数据计算和修改的核心。它支持多种内置函数,包括数学函数(如 intSum、doubleSum、min、max、abs 等)、字符串函数、逻辑函数等。
- =functionName(…):这是在 modify 操作中调用 Jolt 函数的语法。
- @(level, fieldName):这种语法允许您在 modify 操作中引用输入 JSON 中的特定字段,即使该字段不在当前处理的上下文中。level 指示向上回溯的层级。
总结
通过本教程,我们学习了如何使用 Jolt 的 shift 和 modify-overwrite-beta 操作来对 JSON 对象中的数值进行求和,并将结果作为一个新字段添加。这种多阶段的转换方法在 Jolt 中非常常见,能够帮助我们灵活地处理各种复杂的数据转换需求。理解每个操作的职责以及 Jolt 路径表达式和内置函数的使用是掌握 Jolt 转换的关键。