Search code examples
muletransformdataweavemulesoftmule4

Convert XML to JSON with Attributes and Text as Keys Without __text


I have a payload in XML format, and I need to convert it to JSON. The XML contains many key-value pairs and also a lot of attributes, and I need those attributes to be included in the JSON as well.

<CustomData>

  <CustomIdentifier attribute1="value1" attribute2="value2">

    test  </CustomIdentifier>

</CustomData>

This one output writeAttributes=true makes __text but I don't want have it. I want to change this dynamically so that this __text becomes a key. I have more attributes and I can't have everywhere __text

example:

{

"CustomData": {

"CustomIdentifier": {

"@attribute1": "value1",

"@attribute2": "value2",

"CustomIdentifier": "test"

}

}

}

Solution

  • The built-in feature doesn't allow for customization of the output. To achieve the expected output you need a custom transformation. It can be implemented by creating a recursive function that when it receives an object with attributes outputs an object with the current value under a new key with the same name than the existing key, and concatenating the attributes as new key-values. Otherwise return the value applying recursively the same transformation. Any other type is returning as is. My implementation includes mapping each element of an array though that there are no arrays in XML inputs. It should work with other format but I haven't tested it.

    Example

    Function:

    %dw 2.0
    output application/json 
    fun writeWithAttributes(x) = x match {
        case o is Object -> 
            o mapObject ((value, key, index) ->
                if (isEmpty(key.@)) // if there are no attributes just return the same key and transform the value
                    { (key): writeWithAttributes(value) } 
                else // if there are attributes add a new child object with the keys collected and the value repeating the same key.
                    {
                        (key): { 
                            (key): writeWithAttributes(value), 
                            (key.@ mapObject ("@"++$$ as String): $) 
                        }
                    } 
            )
    
        case a is Array -> a map writeWithAttributes($)
        else -> x
      }
    ---
    writeWithAttributes(payload) 
    

    Input:

    <CustomData>
      <CustomIdentifier attribute1="value1" attribute2="value2">test</CustomIdentifier>
    </CustomData>
    

    Output:

    {
      "CustomData": {
        "CustomIdentifier": {
          "CustomIdentifier": "test",
          "@attribute1": "value1",
          "@attribute2": "value2"
        }
      }
    }