node.jsjsonxml-parsingxml2jsxml-builder

JSON to XML conversion in Node JS service (using xml2js)


I need some help/advice with JSON to XML conversion in Node js. I have a service that gets a JSON object in request body that needs to convert to XML. I am able to achieve this using node-xml2js for json inputs with maximum one level of nested objects. But, it gets way more complicated with nested objects having attribute values. Attributes should be identified first, prefixed with $ sign and enclosed in curly braces before parsing through xml2js to get correct xml. Is there a better way of doing this where this complicated layer of reformatting the json input can be simplified? xml2js can converts this:

{
    "Level1":{  "$":{   "attribute":"value" },
                "Level2": {"$":{"attribute1":"05/29/2020",
                            "attribute2":"10","attribute3":"Pizza"}}
}

to this:(which is correct):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Level1 attribute="value">
  <Level2 attribute1="05/29/2020" attribute2="10" attribute3="Pizza"/>
</Level1>

But actual json input is this:

{
    "Level1":{"attribute":"value",
              "Level2": {"attribute1":"05/29/2020",
                         "attribute2":"10","attribute3":"Pizza"} }
}

Expected same result:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Level1 attribute="value">
  <Level2 attribute1="05/29/2020" attribute2="10" attribute3="Pizza"/>
</Level1>

Please let me know if you have worked on similar requirements. Appreciate any help. Thank you.


Solution

  • This would be a way to change the object back to the format expected in the library, although it assumes that all non object keys are supposed to be attributes (is that a valid assumption for your application?)

    function groupChildren(obj) {
      for(prop in obj) { // consider filtering for own properties (vs from prototype: for(prop of Object.keys(obj)) {
        if (typeof obj[prop] === 'object') {
          groupChildren(obj[prop]);
        } else {
          obj['$'] = obj['$'] || {};
          obj['$'][prop] = obj[prop];
          delete obj[prop];
        }
      }
    
      return obj;
    }
    

    and then used like so:

    var xml2js = require('xml2js'); 
    var obj = {
      Level1: {
        attribute: 'value',
        Level2: {
          attribute1: '05/29/2020',
          attribute2: '10',
          attribute3: 'Pizza'
        }
      }
    };
    
    var builder = new xml2js.Builder();
    var xml = builder.buildObject(groupChildren(obj));
    

    which prints out:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <Level1 attribute="value">
      <Level2 attribute1="05/29/2020" attribute2="10" attribute3="Pizza"/>
    </Level1>