Unflatten a JSON Object into nested JSON Object using Dataweave 2.0 Unflatten a JSON Object into nested JSON Object using Dataweave 2.0 json json

Unflatten a JSON Object into nested JSON Object using Dataweave 2.0


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.0var test = {    "abc.def.ghi": "foo",    "abc.def.jkl": "bar"}import * from dw::core::Stringsoutput application/jsonfun generateArray (obj) = obj pluck (v, k) -> (k): vfun isSubChildExists (key) = (((key) splitBy ("."))[1] != null)fun propToJSON(key, value) = if (isSubChildExists(key)) {   (substringBefore(key, ".")) : propToJSON(substringAfter(key, "."), value)}else   (key): valuefun 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"    }  }}


This is similar to @olamiral solution, but simplified and supports arrays.

%dw 2.0output 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 itvar 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.


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.0output application/jsonimport * from dw::util::Valuesfun 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)    })