Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unflatten a JSON Object into nested JSON Object using Dataweave 2.0

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, i.e.

{
  "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"
    }
  }
}
like image 487
Harshank Bansal Avatar asked Apr 19 '26 05:04

Harshank Bansal


2 Answers

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)
    })
like image 165
machaval Avatar answered Apr 21 '26 23:04

machaval


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.

like image 43
Jorge Garcia Avatar answered Apr 21 '26 21:04

Jorge Garcia