0

I have an XML like below.

<Data>
 <Person>
  <Name>ABC</name>
  <Transaction>123</Transaction>
 </Person>

 <Person>
  <Name>ABC</name>
  <Transaction>999</Transaction>
 </Person>

 <Person>
  <Name>XYZ</name>
  <Transaction>123</Transaction>
 </Person>

</Data>

And I am trying to group the XML based on name and produce the output like below

<Data>
<Person>
<Name>ABC</name>
<Transaction>123</Transaction>
<Transaction>999</Transaction>
</Person>

<Person>
<Name>XYZ</name>
<Transaction>123</Transaction>
</Person>

</Data>

How do I achieve this through JavaScript. I tried by comparing data value in innerhtml and for loops. But it is getting very complex. Any other viable solutions?

Thanks.

AbsoluteBeginner
  • 2,160
  • 3
  • 11
  • 21
Manick
  • 3
  • 1
  • 3
  • Familiarize yourself with [how to access and process nested objects, arrays or JSON](/q/11922383/4642212) and how to [create objects](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) and use the available [`Object`](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Static_methods) and [`Array`](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Static_methods) methods (both static and on prototype). – Sebastian Simon May 06 '21 at 07:31

1 Answers1

0

The caveat here is that you're doing this in the browser. JS doesn't treat XML natively, so if you can always prefer your data in JSON format. In the browser, however, we can make use of the DOMParser and XMLSerializer:

const xml = `<Data>
                <Person>
                    <Name>ABC</Name>
                    <Transaction>123</Transaction>
                </Person>

                <Person>
                    <Name>ABC</Name>
                    <Transaction>999</Transaction>
                </Person>

                <Person>
                    <Name>XYZ</Name>
                    <Transaction>123</Transaction>
                </Person>
            </Data>`

// parse our xml to a an xmldocument
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xml, "text/xml");

// extract all the "Person nodes" and store them in a JS object
const peopleJS = {};
const people = [...xmlDoc.getElementsByTagName('Person')]
people.forEach(person => {
    const name = person.getElementsByTagName('Name')[0].innerHTML;
    const transaction = person.getElementsByTagName('Transaction')[0].innerHTML;
    if (name in peopleJS) {
        peopleJS[name].transactions.push(transaction);
    } else {
        peopleJS[name] = {
            name,
            transactions: [transaction]
        }
    }
})

// create a new XMLDocument
const doc = document.implementation.createDocument(null, 'Data');

// go through our JS object, and create our elements
for (let key in peopleJS) {
    const personObj = peopleJS[key];

    // the main Person element
    const personEl = doc.createElement('Person');

    // the Name element
    const nameEl = doc.createElement('Name');
    nameEl.innerHTML = personObj.name;
    personEl.appendChild(nameEl);

    // each Tranaction element for that person
    personObj.transactions.forEach(transaction => {
        const transactionEl = doc.createElement('Transaction');
        transactionEl.innerHTML = transaction;
        personEl.appendChild(transactionEl);
    })

    // attach the Person to our document
    doc.documentElement.appendChild(personEl);
}

// write it back out to a string
const serializer = new XMLSerializer();
console.log(serializer.serializeToString(doc))

So basically, we take our xml data and parse it to return an XMLDocument. This lets us use the normal getElementBy... methods that we're used to, so we don't have to deal with regexs or anything.

NOTE: in your original example, your Name element is written like <Name>ABC</name>, which isn't valid XML, so I had to change the closing tag from </name> to </Name>

Once the document is parsed, we run through it and pull out all of our information and store it in a temporary JS object. When that's done, we'll have a JS object in the form

{
    "ABC": {
        "name": "ABC",
        "transactions": [
            "123",
            "999"
        ]
    },
    "XYZ": {
        "name": "XYZ",
        "transactions": [
            "123"
        ]
    }
}

Now all that's left is to transfer that object back to XML. To be honest, it'd nearly almost be easier to just run through the array and write it out as a string, but we'll do the DOM way for the sake of completeness.

We create a new XMLDocument (it's important that it's XML if you want to keep the case sensitivity (i.e. Data and not data)), run through our JS object and create elements for each of our properties.

Finally, we use XMLSerializer to serialize our new XMLDocument back out to a string.

divillysausages
  • 7,883
  • 3
  • 25
  • 39