使用 Dataweave 2.0 将 JSON 对象展开为嵌套的 JSON 对象
Unflatten a JSON Object into nested JSON Object using Dataweave 2.0
我有这个 Flattened Object
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
我想写一个dataweave把它转换成原始对象,即
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
我试图避免对键进行硬编码,因为它是一个相当大的对象,所以我不想要这样的东西:
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
output application/json
---
{
abc: {
def: {
ghi: test."abc.def.ghi",
jkl: test."abc.def.jkl"
}
}
}
我可以使用一些可用的 dataweave 函数的组合来实现这个吗?
这是我目前尝试过的方法:
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
output application/json
---
test mapObject ((value, key) ->
(key as String splitBy ".")[-1 to 0]
reduce ((item, acc = value) ->
(item): acc
/*
First item=ghi,acc=foo => acc = {ghi: "foo"}
next item=def, acc={ghi: "foo"} => acc={def:{ghi:"foo"}}
*/
)
)
但这会生成某种单独的嵌套 JSON 对。这是上面代码的输出:
{
"abc": {
"def": {
"ghi": "foo"
}
},
"abc": {
"def": {
"jkl": "bar"
}
}
}
您可以尝试以下 dataweave 表达式,该表达式基于 Mohammad Mazhar Ansari 撰写的这篇 Apisero 文章:https://apisero.com/property-to-yaml-file-conversion-using-mulesoft/
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
import * from dw::core::Strings
output application/json
fun generateArray (obj) = obj pluck (v, k) -> (k): v
fun isSubChildExists (key) = (((key) splitBy ("."))[1] != null)
fun propToJSON(key, value) = if (isSubChildExists(key)) {
(substringBefore(key, ".")) : propToJSON(substringAfter(key, "."), value)
}
else
(key): value
fun arrToObj(arr) = arr reduce ((env, obj={}) -> obj ++ env)
fun CombineObjBasedOnKey (Obj) =
if (typeOf(Obj) == Array and sizeOf(Obj..) > 1)
((Obj groupBy (item, index) -> keysOf(item)[0]) mapObject ((value, key, index) ->
(if (typeOf(value) == Array)
(key): CombineObjBasedOnKey(value..'$key')
else if (typeOf(value) == String)
value
else
(key): value
) as Object))
else
Obj[0]
---
CombineObjBasedOnKey(generateArray(test) map ((item, index) -> item mapObject ((value, key, index) -> propToJSON((key), value))
))
输出:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
另一种方法是使用 4.3 中引入的 update 运算符。使用此运算符,我们可以执行更新插入(在不存在时插入值或在存在时更新)使用它和减少我们可以从表达式的每个部分开始并进行适当的更新
%dw 2.0
output application/json
import * from dw::util::Values
fun upsert(object: {}, path:Array<String>, value: Any): Object = do {
path match {
case [] -> object
case [x ~ xs] ->
if(isEmpty(xs))
object update {
case ."$(x)"! -> value
}
else
object update {
case selected at ."$(x)"! ->
//selected is going to be null when the value is not present
upsert(selected default {}, xs, value)
}
}
}
---
payload
pluck ((value, key, index) -> {key: key, value: value})
reduce ((item, resultObject = {} ) -> do {
upsert(resultObject, (item.key as String splitBy '.') , item.value)
})
这类似于@olamiral 解决方案,但简化并支持数组。
%dw 2.0
output application/json
// Creates a array of key-value tuples with the object structure.
// I was not able to use entriesOf() because I had to modify the key to split it
var tuples = payload pluck ((value, key, index) ->
{
k: key splitBy("."),
v: value}
)
// Using groupBy, group the childs and maps to an object structure.
fun flatToObject(tuples, index) =
(tuples groupBy $.k[index]) mapObject ((groupedTuples, key, idx) ->
if(groupedTuples[0].k[index + 1]?)
// Has more levels
{ (key): flatToObject(groupedTuples,index + 1) }
else
// It's a leaf
{ (key): if (sizeOf(groupedTuples.v) > 1)
// It has an array of values
groupedTuples.v
else
// It has a single value
groupedTuples.v[0]
}
)
---
flatToObject(tuples,0)
使用此负载:
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar",
"abc.de.f": "bar2",
"abc.def.jkm": "bar3",
"abc.de.f": 45
}
它正在产生这个输出:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar",
"jkm": "bar3"
},
"de": {
"f": ["bar2", 45]
}
}
}
此解决方案不支持在同一个数组中混合简单值和对象。
我有这个 Flattened Object
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
我想写一个dataweave把它转换成原始对象,即
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
我试图避免对键进行硬编码,因为它是一个相当大的对象,所以我不想要这样的东西:
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
output application/json
---
{
abc: {
def: {
ghi: test."abc.def.ghi",
jkl: test."abc.def.jkl"
}
}
}
我可以使用一些可用的 dataweave 函数的组合来实现这个吗?
这是我目前尝试过的方法:
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
output application/json
---
test mapObject ((value, key) ->
(key as String splitBy ".")[-1 to 0]
reduce ((item, acc = value) ->
(item): acc
/*
First item=ghi,acc=foo => acc = {ghi: "foo"}
next item=def, acc={ghi: "foo"} => acc={def:{ghi:"foo"}}
*/
)
)
但这会生成某种单独的嵌套 JSON 对。这是上面代码的输出:
{
"abc": {
"def": {
"ghi": "foo"
}
},
"abc": {
"def": {
"jkl": "bar"
}
}
}
您可以尝试以下 dataweave 表达式,该表达式基于 Mohammad Mazhar Ansari 撰写的这篇 Apisero 文章:https://apisero.com/property-to-yaml-file-conversion-using-mulesoft/
%dw 2.0
var test = {
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
import * from dw::core::Strings
output application/json
fun generateArray (obj) = obj pluck (v, k) -> (k): v
fun isSubChildExists (key) = (((key) splitBy ("."))[1] != null)
fun propToJSON(key, value) = if (isSubChildExists(key)) {
(substringBefore(key, ".")) : propToJSON(substringAfter(key, "."), value)
}
else
(key): value
fun arrToObj(arr) = arr reduce ((env, obj={}) -> obj ++ env)
fun CombineObjBasedOnKey (Obj) =
if (typeOf(Obj) == Array and sizeOf(Obj..) > 1)
((Obj groupBy (item, index) -> keysOf(item)[0]) mapObject ((value, key, index) ->
(if (typeOf(value) == Array)
(key): CombineObjBasedOnKey(value..'$key')
else if (typeOf(value) == String)
value
else
(key): value
) as Object))
else
Obj[0]
---
CombineObjBasedOnKey(generateArray(test) map ((item, index) -> item mapObject ((value, key, index) -> propToJSON((key), value))
))
输出:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
另一种方法是使用 4.3 中引入的 update 运算符。使用此运算符,我们可以执行更新插入(在不存在时插入值或在存在时更新)使用它和减少我们可以从表达式的每个部分开始并进行适当的更新
%dw 2.0
output application/json
import * from dw::util::Values
fun upsert(object: {}, path:Array<String>, value: Any): Object = do {
path match {
case [] -> object
case [x ~ xs] ->
if(isEmpty(xs))
object update {
case ."$(x)"! -> value
}
else
object update {
case selected at ."$(x)"! ->
//selected is going to be null when the value is not present
upsert(selected default {}, xs, value)
}
}
}
---
payload
pluck ((value, key, index) -> {key: key, value: value})
reduce ((item, resultObject = {} ) -> do {
upsert(resultObject, (item.key as String splitBy '.') , item.value)
})
这类似于@olamiral 解决方案,但简化并支持数组。
%dw 2.0
output application/json
// Creates a array of key-value tuples with the object structure.
// I was not able to use entriesOf() because I had to modify the key to split it
var tuples = payload pluck ((value, key, index) ->
{
k: key splitBy("."),
v: value}
)
// Using groupBy, group the childs and maps to an object structure.
fun flatToObject(tuples, index) =
(tuples groupBy $.k[index]) mapObject ((groupedTuples, key, idx) ->
if(groupedTuples[0].k[index + 1]?)
// Has more levels
{ (key): flatToObject(groupedTuples,index + 1) }
else
// It's a leaf
{ (key): if (sizeOf(groupedTuples.v) > 1)
// It has an array of values
groupedTuples.v
else
// It has a single value
groupedTuples.v[0]
}
)
---
flatToObject(tuples,0)
使用此负载:
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar",
"abc.de.f": "bar2",
"abc.def.jkm": "bar3",
"abc.de.f": 45
}
它正在产生这个输出:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar",
"jkm": "bar3"
},
"de": {
"f": ["bar2", 45]
}
}
}
此解决方案不支持在同一个数组中混合简单值和对象。