How to convert an array of JSON objects to valid XML?

Viewed 139

Let's say I got an array of JSON objects like this:

const cars = [
    {
        "color": "purple",
        "type": "minivan",
        "registration": "2020-02-03",
        "capacity": 7
      },
      {
        "color": "orange",
        "type": "SUV",
        "registration": "2021-05-17",
        "capacity": 4
      },
      {
        "color": "green",
        "type": "coupe",
        "registration": "2019-11-13",
        "capacity": 2
      }

];

I want to convert this object to a valid XML. But the helper packages I use start tags as array indexes when converting it, and therefore the resulting XML is not valid.

For example: (function json2xml from goessner)

function json2xml(o, tab) {
    var toXml = function(v, name, ind) {
       var xml = "";
       if (v instanceof Array) {
          for (var i=0, n=v.length; i<n; i++)
             xml += ind + toXml(v[i], name, ind+"\t") + "\n";
       }
       else if (typeof(v) == "object") {
          var hasChild = false;
          xml += ind + "<" + name;
          for (var m in v) {
             if (m.charAt(0) == "@")
                xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
             else
                hasChild = true;
          }
          xml += hasChild ? ">" : "/>";
          if (hasChild) {
             for (var m in v) {
                if (m == "#text")
                   xml += v[m];
                else if (m == "#cdata")
                   xml += "<![CDATA[" + v[m] + "]]>";
                else if (m.charAt(0) != "@")
                   xml += toXml(v[m], m, ind+"\t");
             }
             xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "</" + name + ">";
          }
       }
       else {
          xml += ind + "<" + name + ">" + v.toString() +  "</" + name + ">";
       }
       return xml;
    }, xml="";
    for (var m in o)
       xml += toXml(o[m], m, "");
    return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
 }
 


const cars = [
{
    "color": "purple",
    "type": "minivan",
    "registration": "2020-02-03",
    "capacity": 7
  },
  {
    "color": "orange",
    "type": "SUV",
    "registration": "2021-05-17",
    "capacity": 4
  },
  {
    "color": "green",
    "type": "coupe",
    "registration": "2019-11-13",
    "capacity": 2
  }

];

console.log(json2xml(cars, ' '));

I tried xml2js, fast-xml-parser and jstoxml packages. Result is the same.

Edit:

Expected XML:

<element> 
    <color>purple</color>
    <type>minivan</type>
    <registration>2020-02-03</registration>
    <capacity>7</capacity>
</element>
<element>
    <color>orange</color>
    <type>SUV</type>
    <registration>2021-05-17</registration>
    <capacity>4</capacity>
</element>
<element>
    <color>green</color>
    <type>coupe</type>
    <registration>2019-11-13</registration>
    <capacity>2</capacity>
</element>
3 Answers

How about this?

This assumes that the array of JSON objects all have the same keys.

let xml = "";

const prefixXML = `<?xml version="1.0" encoding="UTF-8"?>\n<cars>\n`;
const suffixXML = "\n</cars>";

const keys = Object.keys(cars[0]);

cars.forEach((car) => {
  let valueXML = keys
    .map((key) => {
      return `<${key}>${car[key]}</${key}>`;
    })
    .join("\n\t");
  xml += `
    <car>
      ${valueXML}
    </car>
  `;
});

const final = prefixXML + xml + suffixXML;

console.log(final);

The XML output is:

<?xml version="1.0" encoding="UTF-8"?>
<cars>

    <car>
        <color>purple</color>   
    <type>minivan</type>    
    <registration>2020-02-03</registration> 
    <capacity>7</capacity>  
    </car>
  
    <car>
        <color>orange</color>   
    <type>SUV</type>    
    <registration>2021-05-17</registration> 
    <capacity>4</capacity>  
    </car>
  
    <car>
        <color>green</color>    
    <type>coupe</type>  
    <registration>2019-11-13</registration> 
    <capacity>2</capacity>  
    </car>
  
</cars> 

If you're using FXP (fast-xml-parser), you can use folloing configuration to avoid array index as tag name

const parser = new j2xParser({
    rootNodeName: "car"
});
const result = parser.parse(cars);

FXP version must be > 3.21.0

I'm the creator of jstoxml. Just for reference, this is how you'd structure your object to pass into jstoxml:

const { toXML } = window.jstoxml;

const cars = [
    {
        _name: 'element',
        _content: {
            color: 'purple',
            type: 'minivan',
            registration: '2020-02-03',
            capacity: 7
        }
    },
    {
        _name: 'element',
        _content: {
            color: 'orange',
            type: 'SUV',
            registration: '2021-05-17',
            capacity: 4
        }
    },
    {
        _name: 'element',
        _content: {
            color: 'green',
            type: 'coupe',
            registration: '2019-11-13',
            capacity: 2
        }
    }
];

console.log(toXML(cars, { indent: '    ' }));

/**
Output:

<element>
    <color>purple</color>
    <type>minivan</type>
    <registration>2020-02-03</registration>
    <capacity>7</capacity>
</element>
<element>
    <color>orange</color>
    <type>SUV</type>
    <registration>2021-05-17</registration>
    <capacity>4</capacity>
</element>
<element>
    <color>green</color>
    <type>coupe</type>
    <registration>2019-11-13</registration>
    <capacity>2</capacity>
</element>
*/
<script src="https://cdn.jsdelivr.net/npm/jstoxml@2.2.4/dist/jstoxml.min.js"></script>

Related