
在数据处理领域,经常会遇到需要对json结构中的特定数值进行聚合计算,并将其结果整合到输出数据中的场景。jolt作为一款强大的json转换工具,能够通过一系列声明式的转换操作(spec)高效地完成这类任务。本文将以一个具体的例子,详细讲解如何利用jolt实现对json对象内部所有数值的求和,并将求和结果作为一个新字段添加到原始数据中。
场景描述
假设我们有如下输入JSON数据,其中accounts对象包含多个账户及其对应的数值:
输入JSON:
{
"accounts": {
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0
}
}我们的目标是计算accounts对象中所有数值(1, 0, 1, 0)的总和,并将结果2添加为一个名为"total accounts"的新字段,同时保留原始的账户信息,最终输出结构如下:
期望输出JSON:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"total accounts": 2
}Jolt转换规范解析
为了实现上述转换,我们需要构建一个包含多个操作的Jolt Spec。这个过程可以分为三个主要阶段:数据提取与暂存、数值计算与新字段生成、结构重塑与清理。
阶段一:数据提取与暂存
首先,我们需要将accounts对象中的所有值提取出来,以便后续进行求和。同时,为了在最终输出中保留原始的账户信息,我们也需要将其暂存。
[
{
"operation": "shift",
"spec": {
"accounts": {
"*": {
"@": "&",
"@": "accountsAccumulator[]"
}
}
}
},
{
// ... 后续操作
}
]操作详解:
- "operation": "shift":这是一个核心操作,用于重塑JSON结构。
- "accounts": {...}:匹配输入JSON中的accounts字段。
- "*": {...}:匹配accounts对象下的所有键(例如canara、sbi等)。
- "@": "&":这是关键。@代表当前匹配到的值(例如1、0)。&代表当前匹配到的键(例如canara、sbi)。这个规则的含义是将原始的键值对(如"canara": 1)直接提升到输出的根层级。
- "@": "accountsAccumulator[]":这会将当前匹配到的值(如1、0)收集到一个名为accountsAccumulator的数组中。[]表示这是一个数组,每次匹配都会向其中添加一个元素。
经过此阶段,中间输出可能类似:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"accountsAccumulator": [1, 0, 1, 0]
}阶段二:数值计算与新字段生成
接下来,我们利用modify-overwrite-beta操作对暂存的数值数组进行求和,并将结果赋值给新的字段。
[
{
"operation": "shift",
"spec": {
"accounts": {
"*": {
"@": "&",
"@": "accountsAccumulator[]"
}
}
}
},
{
"operation": "modify-overwrite-beta",
"spec": {
"total accounts": "=intSum(@(1,accountsAccumulator))"
}
},
{
// ... 后续操作
}
]操作详解:
- "operation": "modify-overwrite-beta":这个操作允许我们对现有数据进行修改或创建新字段,并支持数学函数。
- "total accounts": "=intSum(@(1,accountsAccumulator))":
- "total accounts":定义了要创建的新字段的名称。
- =intSum(...):调用Jolt内置的intSum函数进行整数求和。
- @(1,accountsAccumulator):这是一个路径表达式。@表示引用数据,1表示向上回溯一级(即从当前modify操作的输入中查找),accountsAccumulator是我们要求和的数组的名称。这意味着它会获取上一阶段生成的accountsAccumulator数组并对其进行求和。
经过此阶段,中间输出可能类似:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"accountsAccumulator": [1, 0, 1, 0],
"total accounts": 2
}阶段三:结构重塑与清理
最后一步是清理临时字段accountsAccumulator,确保输出只包含我们期望的数据。
[
{
"operation": "shift",
"spec": {
"accounts": {
"*": {
"@": "&",
"@": "accountsAccumulator[]"
}
}
}
},
{
"operation": "modify-overwrite-beta",
"spec": {
"total accounts": "=intSum(@(1,accountsAccumulator))"
}
},
{
"operation": "shift",
"spec": {
"accountsAccumulator": null,
"*": "&"
}
}
]操作详解:
- "operation": "shift":再次使用shift操作进行最终的结构调整。
- "accountsAccumulator": null:这会将accountsAccumulator字段从输出中移除。
- "*": "&":这个通配符规则将所有其他根级别的字段(如canara、sbi、total accounts)保留并提升到输出的根层级。
完整的Jolt Spec
将以上三个阶段的Spec组合起来,就得到了最终的Jolt转换规范:
[
{
"operation": "shift",
"spec": {
"accounts": {
"*": {
"@": "&",
"@": "accountsAccumulator[]"
}
}
}
},
{
"operation": "modify-overwrite-beta",
"spec": {
"total accounts": "=intSum(@(1,accountsAccumulator))"
}
},
{
"operation": "shift",
"spec": {
"accountsAccumulator": null,
"*": "&"
}
}
]示例与效果
使用上述Jolt Spec对以下输入JSON进行转换:
输入:
{
"accounts": {
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0
}
}将得到期望的输出:
输出:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"total accounts": 2
}注意事项
- 数据类型: intSum函数用于整数求和。如果您的数值可能包含小数,应使用doubleSum函数。
- 临时字段: 在Jolt转换中,创建临时字段(如本例中的accountsAccumulator)是实现复杂逻辑的常见模式。在转换的最后阶段,务必清理这些临时字段以保持输出的整洁。
- 路径表达式: @(1,accountsAccumulator)中的@(1,...)表示从当前节点向上回溯一级查找指定的字段。理解Jolt的路径表达式对于编写复杂的Spec至关重要。
- 健壮性: 本例假设accounts下的值都是数字。如果存在非数字值,intSum或doubleSum可能会忽略它们或导致错误,具体行为取决于Jolt版本和配置。在生产环境中,可能需要额外的步骤来验证或清洗数据。
总结
通过本教程,我们学习了如何利用Jolt的shift和modify-overwrite-beta操作,实现对JSON对象内部数值的求和,并将结果整合到新的字段中,同时保持原始数据的完整性。这种分阶段、模块化的Jolt Spec设计方法,有助于处理更复杂的数据转换需求,使JSON数据处理变得更加灵活和高效。掌握这些基本模式,将极大地提升您在数据集成和转换项目中的工作效率。










