xmlmuledataweavemulesoftmule4

JSON conversion to XML using attributes and namespaces


I have a JSON payload that I need to convert into an XML. However that JSON payload has many fields starting with '@' which are supposed to be the XML attributes. So when I converted those into XML the '@" is being attached to each xml tag as a field(e.g. <@id>1495161</@id>). Each field with '@' is supposed to be the XML attribute and should not come as indivial XML filed/tag after the conversion. Please see my code and output vs the expected output. Notice how all the fields with @ are transformed into xml tag. But I want all these fields with @ to transform into XML attributes.

Sample Request:

{
    "trans": {
        "@id": "1495144",
        "@TPId": "4aec",
        "@change": "0",
        "@count": "1",
        "@dateStamp": "2024-08-02T03:07:48",
        "data": {
            "@id": "d7D173564C5F14FF2AD08D621C188AF19",
                "StateFlag": "0",
                "DCC": "B",
                "LastName": "MMNGorthy",
                "Name": "Scott",
                "Structure": "NonProfit",
                "BusinessType": "Engineering",
                "meams": "AI",
                "Count": "0",
                "CTier": "000",
                "CTCond": "111",
                "CTPl": "222",
                "DO": "1",
                "AuthObject": "0"
            }
        }
    }

Code:

%dw 2.0
output application/xml
ns ns0 http://Test.Sample.Data/1.0
---

    ns0#trans: payload.trans

output from my code:

<?xml version='1.0' encoding='UTF-8'?>
<ns0:trans xmlns:ns0="http://Test.Sample.Data/1.0">
  <@id>1495144</@id>
  <@TPId>4aec</@TPId>
  <@change>0</@change>
  <@count>1</@count>
  <@dateStamp>2024-08-02T03:07:48</@dateStamp>
  <data>
    <@id>d7D173564C5F14FF2AD08D621C188AF19</@id>
    <StateFlag>0</StateFlag>
    <DCC>B</DCC>
    <LastName>MMNGorthy</LastName>
    <Name>Scott</Name>
    <Structure>NonProfit</Structure>
    <BusinessType>Engineering</BusinessType>
    <meams>AI</meams>
    <Count>0</Count>
    <CTier>000</CTier>
    <CTCond>111</CTCond>
    <CTPl>222</CTPl>
    <DO>1</DO>
    <AuthObject>0</AuthObject>
  </data>
</ns0:trans>

Expected output:

  <?xml version='1.0' encoding='UTF-8'?>
<ns0:trans xmlns:ns0="http://Test.Sample.data/1.0" id="1495144" TPId='4aec' change='0' count='1' dateStamp="2024-08-02T03:07:48">
    <data id="FF2AD08D621">
        <StateFlag>0</StateFlag>
        <DCC>B</DCC>
        <LastName>MMNGorthy</LastName>
        <Name>Scott</Name>
        <Structure>NonProfit</Structure>
        <BusinessType>Engineering</BusinessType>
        <meams>AI</meams>
        <Count>0</Count>
        <CTier>000</CTier>
        <CTCond>111</CTCond>
        <CTPl>222</CTPl>
        <DO>1</DO>
        <AuthObject>0</AuthObject>
    </data>
</ns0:trans>

Solution

  • To add an attribute, you need to define them as:

    xmlElem @(att1: val1, att2: val2): xmlValue
    

    Notice that the value inside the @() is not an object but the deconstructed version of the object. For example, if you have:

    var jsonObject = {att1: val1, att2: val2}
    

    You cannot directly write:

    xmlElem @(jsonObject): xmlValue
    

    Instead, you need to deconstruct it using ():

    xmlElem @((jsonObject)): xmlValue
    

    Now, for your use case, you need to make the output dynamic. You can use the following transformation. Most of the functions are self explanatory.

    %dw 2.0
    output application/xml
    ns ns0 http://Test.Sample.Data/1.0
    
    fun getAttributes(jsonObject: Object) = 
        jsonObject 
            filterObject ($$ startsWith "@")
            mapObject ((value, key) -> (key[1 to -1]): value) //remove first char from key i.e. @
    
    fun getNonAttributes(jsonObject: Object) = 
        jsonObject 
            filterObject !($$ startsWith "@")
    
    fun fromJsonToXml(json) = 
        json mapObject ((value, key) -> 
            if(value is Object) (key) @((getAttributes(value))): fromJsonToXml(getNonAttributes(value)) // Attributes are applied using `@( (getAttributes(value)) )`. Notice how the function call is further wrapped around `()` to deconstruct the output
            else (key): value
        )
    ---
    // following is to add namespace
    fromJsonToXml(payload) 
    then ((resultWithoutNS) -> 
        ns0#trans @((resultWithoutNS.trans.@)): resultWithoutNS.trans
    )