I have this Flattened Object
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar"
}
I want to write a dataweave to convert it into the original object, ie
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
I am trying to avoid hard coding the keys because it is a quite large object, So I do not want something like this:
%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"
}
}
}
Can I use some combination of available dataweave functions for achieving this?
Here is what I have tried so far:
%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"}}
*/
)
)
But this would generate some kind of separate pair of the nested JSON. Here is the output of the above code:
{
"abc": {
"def": {
"ghi": "foo"
}
},
"abc": {
"def": {
"jkl": "bar"
}
}
}
You can try the following dataweave expression, based on this Apisero article, written by Mohammad Mazhar Ansari: 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))
))
Output:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar"
}
}
}
Another way to do this is to use the update operator that was introduce in 4.3. With this operator we can do upsert (insert the value when is not present or update if present) Using that and a reduce we can go from each of the part of the expression and do the proper 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)
})
This is similar to @olamiral solution, but simplified and supports arrays.
%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)
With this payload:
{
"abc.def.ghi": "foo",
"abc.def.jkl": "bar",
"abc.de.f": "bar2",
"abc.def.jkm": "bar3",
"abc.de.f": 45
}
It's producing this output:
{
"abc": {
"def": {
"ghi": "foo",
"jkl": "bar",
"jkm": "bar3"
},
"de": {
"f": ["bar2", 45]
}
}
}
This solution does not support mixing simple values and objects in the same array.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.