
1. Jolt 转换简介
jolt 是一种强大的 json 到 json 转换工具,它允许开发者通过定义一系列的“转换规范”(jolt spec)来重塑、过滤、修改 json 数据结构。在处理异构数据源或需要将数据适配到特定格式的场景中,jolt 提供了灵活且声明式的方法。
2. 复杂 JSON 结构转换挑战
在实际应用中,我们经常面临需要从复杂的嵌套 JSON 结构中提取特定信息,并将其重塑为另一种格式的挑战。本教程将以一个具体的案例为例,展示如何处理包含动态数组、需要字段重命名和数据类型转换的场景。
原始输入 JSON 示例:
{
"Entity": {
"card": {
"cardNo": "123456789",
"cardStatus": "10",
"cardAddress": "UK",
"cardAddress1": "US",
"cardCity": "mk",
"name": "RAM",
"lastName": "ABU",
"name1": "RAM1",
"lastName1": "ABU1"
},
"Photos": [
{
"Id": 327703,
"Caption": "TEST>> photo 1",
"Url": "http://bob.com/0001/327703/photo.jpg"
},
{
"Id": 327704,
"Caption": "TEST>> photo 2",
"Url": "http://bob.com/0001/327704/photo.jpg"
},
{
"Id": 327704,
"Caption": "TEST>> photo 2",
"Url": "http://bob.com/0001/327704/photo.jpg"
}
]
}
}期望输出 JSON 示例:
{
"tab": {
"text": "123456789"
},
"address": [
{
"add": "UK",
"add2": "US",
"mk": "mk"
}
],
"Photos": [
{
"caption2": "http.1.com",
"no": "222444"
},
{
"caption2": "http.2.com",
"no": "222444"
},
{
"caption2": "TEST>> photo 1",
"no": "327703"
},
{
"caption2": "TEST>> photo 2",
"no": "327704"
},
{
"caption2": "TEST>> photo 2",
"no": "327704"
}
]
}从上述输入和输出可以看出,核心需求包括:
- 将 Entity.card 中的字段重命名并归类到 tab 和 address 对象中。
- 处理 Entity.Photos 数组,将其中的 Id 映射为 no,Caption 映射为 caption2。
- 在 Photos 数组中,除了从 Entity.Photos 提取的数据外,还需要根据 card 部分的某些逻辑(此处为示例中的硬编码 Id1, Id2 等)生成额外的 Photos 条目。
- 最重要的是,将 Photos 数组中 no 字段的数值类型转换为字符串类型。
3. 逐步构建 Jolt Spec
我们将分两个主要步骤来构建 Jolt Spec:首先使用 shift 操作进行结构重塑和字段映射,然后使用 modify-overwrite-beta 操作进行数据类型转换。
3.1 步骤一:基础数据重塑 (Shift Operation)
shift 操作是 Jolt 中最常用的操作之一,用于将输入 JSON 中的值移动到输出 JSON 的新路径。它通过键值对的方式定义映射规则,其中键是输入路径模式,值是输出路径模式。
[
{
"operation": "shift",
"spec": {
"Entity": {
"card": {
"cardNo": "tab.text",
"cardAddress": "address[0].add",
"cardAddress1": "address[0].add2",
"cardC*": "address[0].mk",
// 以下是示例中用于生成额外Photos条目的硬编码逻辑
"Id1": "Photos.no",
"#http.1.com": "Photos.caption2",
"Id2": "Photos.no",
"#http.2.com": "Photos.caption2"
},
"Photos": {
"*": { // 使用通配符 * 处理动态数组中的每个元素
"Id": "Photos.no",
"Caption": "Photos.caption2"
}
}
}
}
},
{
"operation": "shift",
"spec": {
"tab": "&", // 将 "tab" 对象提升到根级别
"address": "&", // 将 "address" 数组提升到根级别
"Photos": {
"*": {
"*": {
"@": "&3[&1].&2" // 这是一个相对复杂的路径重组,确保Photos数组的结构正确
}
}
}
}
}
]解释:
-
第一个 shift 阶段:
- "cardNo": "tab.text":将 Entity.card.cardNo 的值移动到 tab.text。
- "cardAddress": "address[0].add":将 Entity.card.cardAddress 移动到 address 数组的第一个元素的 add 字段。[0] 表示创建一个单元素数组。
- "cardC*": "address[0].mk":使用通配符 * 匹配 cardCity,将其值移动到 address[0].mk。
- 硬编码部分 (Id1, #http.1.com 等):这些规则会创建额外的 Photos 数组元素。由于 Id1 和 Id2 在输入中不存在,Jolt 会根据其默认行为为 Photos.no 赋予一个默认值(例如,在某些 Jolt 版本或配置下可能是 0 或一个特定的数字,在我们的示例中为 222444)。#http.1.com 和 #http.2.com 是字面量,直接作为 Photos.caption2 的值。
- "Photos": {"*": {"Id": "Photos.no", "Caption": "Photos.caption2"}}:这是处理动态 Photos 数组的关键。"*" 匹配数组中的每个对象。对于每个对象,Id 的值被移动到 Photos.no,Caption 的值被移动到 Photos.caption2。
-
第二个 shift 阶段:
- "tab": "&" 和 "address": "&":这些规则将前一个 shift 操作中生成的 tab 和 address 对象/数组提升到输出 JSON 的根级别。
- "Photos": {"*": {"*": {"@": "&3[&1].&2"}}}:这一部分旨在确保 Photos 数组的结构正确。它将 Photos 数组中的每个元素的每个字段(@ 代表当前字段的值)移动到 Photos 数组中对应的位置。&3 指向 Photos 自身,&1 指向当前元素的索引,&2 指向当前字段的键。这个复杂的路径表达式确保了 Photos 数组中的每个对象保持其结构。
经过这两个 shift 操作后,输出的 JSON 结构已经基本符合要求,但 Photos 数组中的 no 字段仍然是数字类型。
3.2 步骤二:数据类型转换 (Modify Operation)
为了将 no 字段从数字类型转换为字符串类型,我们需要使用 modify-overwrite-beta 操作。modify 系列操作允许在不改变数据路径的情况下修改字段的值,例如进行类型转换、字符串拼接、数学运算等。
,
{
"operation": "modify-overwrite-beta",
"spec": {
"Photos": {
"*": {
"no": "=toString" // 将 Photos 数组中每个元素的 "no" 字段值转换为字符串
}
}
}
}解释:
- "operation": "modify-overwrite-beta":指定使用 modify-overwrite-beta 操作。这个操作会覆盖现有字段的值。
- "Photos": {"*": {"no": "=toString"}}:
- "Photos":定位到 Photos 数组。
- "*":匹配 Photos 数组中的每一个元素(对象)。
- "no":定位到每个元素中的 no 字段。
- "=toString":这是一个 Jolt 内置的函数,它会将 no 字段的当前值转换为其字符串表示形式,并覆盖原始值。
4. 完整的 Jolt Spec 示例
将上述两个步骤的 Jolt Spec 组合起来,形成一个完整的转换链。Jolt 转换是按顺序执行的,因此 shift 操作完成后,modify 操作会在 shift 的输出结果上执行。
[
{
"operation": "shift",
"spec": {
"Entity": {
"card": {
"cardNo": "tab.text",
"cardAddress": "address[0].add",
"cardAddress1": "address[0].add2",
"cardC*": "address[0].mk",
"Id1": "Photos.no",
"#http.1.com": "Photos.caption2",
"Id2": "Photos.no",
"#http.2.com": "Photos.caption2"
},
"Photos": {
"*": {
"Id": "Photos.no",
"Caption": "Photos.caption2"
}
}
}
}
},
{
"operation": "shift",
"spec": {
"tab": "&",
"address": "&",
"Photos": {
"*": {
"*": {
"@": "&3[&1].&2"
}
}
}
}
},
{
"operation": "modify-overwrite-beta",
"spec": {
"Photos": {
"*": {
"no": "=toString"
}
}
}
}
]使用这个完整的 Jolt Spec 对原始输入 JSON 进行转换,将得到完全符合期望的输出 JSON,其中 Photos 数组中所有 no 字段的值都已成功转换为字符串类型。
5. 注意事项与最佳实践
- *通配符 (`) 的使用:** 在处理动态数组或未知键名时,*` 通配符非常有用。它允许 Jolt 遍历集合中的所有元素或对象的所有字段。
- Jolt 链式操作: Jolt Spec 是一个操作数组,每个操作都会在前一个操作的输出上执行。理解这种链式处理对于构建复杂的转换至关重要。
-
modify 操作家族: 除了 modify-overwrite-beta 和 toString,Jolt 的 modify 操作还提供了其他强大的函数,例如:
- =toInteger: 转换为整数。
- =toDouble: 转换为浮点数。
- =toBoolean: 转换为布尔值。
- =concat(field1, field2, ...): 字符串拼接。
- =split(delimiter): 字符串分割。
- =size: 获取数组或字符串的长度。
- =now(): 获取当前时间戳。
- 这些函数极大地扩展了 Jolt 在数据处理方面的能力。
- 调试 Jolt Spec: 对于复杂的 Jolt Spec,建议分阶段进行测试。可以逐步添加操作,并检查每一步的输出,以确保转换逻辑正确。许多在线 Jolt Transform 调试器可以帮助可视化每一步的转换结果。
- 路径匹配优先级: Jolt 在匹配输入路径时有特定的优先级规则。通常,更具体的路径模式会优先于更通用的通配符模式。
6. 总结
本教程详细展示了如何利用 Jolt 的 shift 和 modify-overwrite-beta 操作来解决复杂的 JSON 转换问题,特别是针对动态数组的处理和数据类型的精确转换。通过理解这些核心概念和实践技巧,开发者可以更有效地使用 Jolt 来满足各种数据集成和转换的需求。掌握 Jolt 不仅能提高数据处理效率,还能增强系统的灵活性和可维护性。










