
Jolt 概述
Jolt 是一个用于 JSON 数据转换的 Java 库,它提供了一系列强大的操作符,如 shift、default、remove、sort、cardinality 和 modify 等,使用户能够声明式地定义 JSON 数据的重塑、过滤和聚合逻辑。Jolt 的核心思想是使用一个 JSON 结构作为“转换规范”(Spec),来描述如何将输入 JSON 转换为目标 JSON。
问题场景:计算嵌套对象数值总和
假设我们有一个包含多个账户余额的 JSON 对象,这些余额都嵌套在 accounts 字段下:
输入 JSON 示例:
{
"accounts": {
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0
}
}我们的目标是保留所有原始账户信息,同时计算 accounts 对象中所有账户余额的总和,并将这个总和以 total accounts 的键名添加到输出 JSON 的根级别。
期望输出 JSON 示例:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"total accounts": 2
}解决方案详解:Jolt 转换规则剖析
为了实现上述转换,我们需要编写一个包含三个操作步骤的 Jolt Spec。
第一步:数据平铺与聚合 (Shift Operation)
此步骤的目的是将 accounts 对象中的每个键值对平铺到根级别,并同时将所有的数值收集到一个临时的数组中,以便后续求和。
{
"operation": "shift",
"spec": {
"accounts": {
"*": [
".&",
"accountsAccumulator"
]
}
}
}- "accounts": 匹配输入 JSON 中的 accounts 字段。
- "*": 匹配 accounts 对象下的所有键(如 "canara", "sbi" 等)。
- [".&", "accountsAccumulator"]: 这是关键部分。
- .&: . 代表当前匹配到的值(即账户余额),& 代表当前匹配到的键(即账户名)。".&" 的作用是将当前键值对(例如 "canara": 1)保持不变,并将其映射到输出的根级别。
- "accountsAccumulator": 将当前匹配到的值(账户余额)收集到一个名为 accountsAccumulator 的数组中。由于 accountsAccumulator 是一个数组,每次匹配到新的值时,都会将其追加到该数组中。
经过此步骤后,中间结果将类似:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"accountsAccumulator": [1, 0, 1, 0]
}第二步:执行求和计算 (Modify-Overwrite-Beta Operation)
在这一步中,我们利用 modify-overwrite-beta 操作符来计算 accountsAccumulator 数组中所有元素的总和,并将其赋值给 total accounts 字段。
{
"operation": "modify-overwrite-beta",
"spec": {
"total accounts": "=intSum(@(1,accountsAccumulator))"
}
}- "total accounts": 定义了我们要创建的新字段。
- "=intSum(@(1,accountsAccumulator))": 这是 Jolt 的内置函数和路径表达式。
- =: 表示这是一个函数调用。
- intSum(): Jolt 提供的一个数学函数,用于计算整数数组的总和。
- @(1,accountsAccumulator): 这是一个相对路径表达式。
- @: 表示从当前位置开始查找。
- 1: 表示向上回溯一层(因为 accountsAccumulator 在上一操作中被放置在根级别,与 total accounts 将在同一级别)。
- accountsAccumulator: 指定要操作的数组字段名。
此步骤将计算 [1, 0, 1, 0] 的总和(结果为 2),并将其添加到 total accounts 字段。
中间结果将变为:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"accountsAccumulator": [1, 0, 1, 0],
"total accounts": 2
}第三步:清理与重塑 (Shift Operation)
最后一步是清理临时生成的 accountsAccumulator 字段,并确保所有其他字段都正确地保留在输出的根级别。
{
"operation": "shift",
"spec": {
"accountsAccumulator": null,
"*": "&"
}
}- "accountsAccumulator": null: 将 accountsAccumulator 字段的值映射为 null,这在 shift 操作中等同于删除该字段。
- "*": "&": 这是一个通配符规则,表示将当前层级(根级别)的所有其他字段(除了 accountsAccumulator)及其值保持不变,并将其映射到输出的根级别。
最终,accountsAccumulator 被移除,只剩下原始账户信息和新计算出的 total accounts 字段。
完整 Jolt 转换规则
将以上三个步骤组合起来,就构成了完整的 Jolt Spec:
[
{
"operation": "shift",
"spec": {
"accounts": {
"*": [
".&",
"accountsAccumulator"
]
}
}
},
{
"operation": "modify-overwrite-beta",
"spec": {
"total accounts": "=intSum(@(1,accountsAccumulator))"
}
},
{
"operation": "shift",
"spec": {
"accountsAccumulator": null,
"*": "&"
}
}
]示例与验证
使用上述 Jolt Spec 对输入 JSON 进行转换:
输入 JSON:
{
"accounts": {
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0
}
}执行 Jolt 转换后,将得到以下输出:
{
"canara": 1,
"sbi": 0,
"axis": 1,
"hdfc": 0,
"total accounts": 2
}这与我们的期望输出完全一致。
注意事项
- Jolt 版本兼容性: modify-overwrite-beta 操作符及其内置函数(如 intSum)在较新的 Jolt 版本中更为稳定和功能完善。请确保您的 Jolt 库版本支持这些特性。
- 数据类型: intSum 函数专门用于整数求和。如果您的数据可能包含浮点数(例如 1.5, 2.0),则应考虑使用 doubleSum 或其他适合浮点数计算的函数。确保被求和的字段值是数字类型,否则可能导致转换失败或结果不准确。
- 临时字段命名: accountsAccumulator 只是一个临时字段名,您可以根据实际情况选择任何不与现有字段冲突的名称。
- 通用性: 这种模式可以推广到其他类似的聚合场景,例如计算平均值、最大值、最小值等,只需替换 modify-overwrite-beta 中的数学函数即可。
总结
通过本教程,我们学习了如何利用 Jolt 的 shift 和 modify-overwrite-beta 操作符,结合内置的数学函数,高效地对 JSON 嵌套对象中的数值进行求和。这种分步转换的策略,即先平铺聚合数据,再进行计算,最后清理临时字段,是 Jolt 处理复杂数据转换任务的典型范式。掌握这些核心概念,将有助于您更灵活地应对各种 JSON 数据转换挑战。










