【问题标题】:Unflatten a JSON Object into nested JSON Object using Dataweave 2.0使用 Dataweave 2.0 将 JSON 对象展开为嵌套的 JSON 对象
【发布时间】:2020-09-10 00:16:43
【问题描述】:

我有这个扁平化的对象

{
  "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"
    }
  }
}

【问题讨论】:

    标签: json mule dataweave mulesoft


    【解决方案1】:

    您可以根据 Mohammad Mazhar Ansari 撰写的这篇 Aisero 文章尝试以下 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))
    ))
    

    输出:

    {
      "abc": {
        "def": {
          "ghi": "foo",
          "jkl": "bar"
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      这类似于@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]
          }
        }
      }
      

      此解决方案不支持在同一个数组中混合简单值和对象。

      【讨论】:

        【解决方案3】:

        另一种方法是使用 4.3 中引入的 update 运算符。使用这个操作符,我们可以做 upsert(当不存在时插入值,如果存在则更新)使用它和 reduce,我们可以从表达式的每个部分进行适当的更新

        %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)
            })
        

        【讨论】:

        • 我真的很想为此使用带有更新的 nee upsert。但是我一直在努力在“case v at .(DYNAMICPATH)”中使用动态值。你发现这个符号了吗?我在任何地方都找不到。
        • 我将查看文档以查看它是否不存在。我知道这一点是因为我写了它;)它不是一条路径,它只是一个字段,这就是我需要递归执行它的原因。
        • 不错!您能否解释一下 case [x ~ xs] 是如何将数组分成两部分的?
        • 这是一个头尾解构。您可以在任何模式匹配中使用它。是一种迭代集合的简单方法。
        猜你喜欢
        • 2021-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-19
        • 2012-05-29
        • 2019-01-17
        • 2019-01-26
        相关资源
        最近更新 更多