简体   繁体   English

使用 Dataweave 2.0 将 JSON 对象展开为嵌套的 JSON 对象

[英]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, ie我想写一个dataweave把它转换成原始对象,即

{
  "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?我可以使用一些可用的 dataweave 函数组合来实现这一点吗?

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.但这会生成某种单独的嵌套 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/您可以根据 Mohammad Mazhar Ansari 撰写的这篇 Apisero 文章尝试以下 dataweave 表达式: 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.另一种方法是使用 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使用这个操作符,我们可以执行 upsert(在不存在时插入值或如果存在则更新)使用它和减少我们可以从表达式的每个部分开始并进行适当的更新

%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.这类似于@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)

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.此解决方案不支持在同一数组中混合简单值和对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM