XML parsing with NodeJS

Kajal SinghKajal Singh
8 min read

Before starting XML with NodeJS you should know the basics of XML, you can refer to XML-Basic Introduction for Beginners for that,

Reading XML file in HTML

Let's start by making a basic .xml file, and displaying XML file data to HTML.

song.xml

<?xml version="1.0" encoding="UTF-8" ?>        //XML declaration
<list>                                         //root element
  <song>                                                                
    <title>1904</title>
    <artist>The Tallest Man on Earth</artist>
    <year>2012</year>
  </song>
  <song>
    <title>#40</title>
    <artist>Dave Matthews</artist>
    <year>1999</year>
  </song>
  <song>
    <title>40oz to Freedom</title>
    <artist>Sublime</artist>
    <year>1996</year>
  </song>
  <song>
    <title>#41</title>
    <artist>Dave Matthews</artist>
    <year>1996</year>
  </song>
</list>

song.html

<html>
<head>
    <style>
        .title,
        .artist {
            margin: auto 0%;
            text-align: center;
            vertical-align: middle;
            font-family: system-ui;
        }
        .title {
            font-weight: 300;
            margin-bottom: 5px;
        }
        .artist {
            color: #04bfbf;
        }
        .year {
            position: absolute;
            top: 98px;
            left: 11px;
            transform: rotate(14deg);
            font-size: 26px;
            height: 450px;
            font-family: system-ui;
        }
        .block {
            position: relative;
            margin: 5px;
            padding: 5px;
            border: 1px solid black;
        }
        table {
            width: 100%;
            text-align: center;
        }
        table tr td {
            display: inline-block;
            width: 224px;
            height: 265px;
        }
        .head {
            font-size: 31px;
            text-align: center;
            font-family: system-ui;
            font-weight: 400;
        }
        .head span {
            border-bottom: 4px solid #04bfbf;
        }
    </style>
</head>

<body>
    <div>
        <h3 class="head"><span>Song lists</span></h3>
        <table id="song-list">
        </table>
    </div>
    <script>
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                displayList(this);
            }
        };
        xhttp.open("GET", "./song.xml", true);
        xhttp.send();

        function displayList(xml) {
            var xmlDoc = xml.responseXML;
            var song = xmlDoc.getElementsByTagName("song");
            var table = '<tr>';
            for (i = 0; i < song.length; i++) {
                table += "<td><div class='block'>" +
                    "<img src='./music.gif' width=200 class='poster'>" +
                    "<p class='year'>" + song[i].getElementsByTagName("year")[0]
                        .childNodes[0].nodeValue + "</p>" +
                    "<p class='title'>" + song[i].getElementsByTagName("title")[0]
                        .childNodes[0].nodeValue +
                    "</p><p class='artist'>" + song[i].getElementsByTagName("artist")[0]
                        .childNodes[0].nodeValue + "</p></div></td>"
            }
            table += '</tr>';
            document.getElementById("song-list").innerHTML = table;
        }
    </script>
</body>
</html>

tip: if you are using vscode then you must install Live Server extension, with this you can run the XML/HTML page just by right click. and one more https://www.convertjson.com/json-to-xml.htm this will help you to convert JSON data in XML format.

Final view image.png

Now, as XML have some set of characters as reserved, if we add those characters in our XML cause i.e.

song.xml

<?xml version="1.0" encoding="UTF-8" ?>
<list>
  <song>
    <title>1904</title>
    <artist>The Tallest Man on Earth</artist>
    <year>2012</year>
  </song>
  <song>
    <title>#40</title>
    <artist>Dave & luv</artist>
    <year>1999</year>
  </song>
  <song>
    <title>40'oz to Freedom</title>
    <artist>Sublime</artist>
    <year>1996</year>
  </song>
</list>

image.png

What is cause the error?

The &, this character is counted as a reserved word. image.png

To solve this XML provides predefined entities,

Character          Entity Reference          Decimal reference          Hexadecimal reference
    "                   &quot;                      &#34;                        &#x22;
    &                    &amp;                      &#38;                        &#x26;
    '                   &apos;                      &#39;                        &#x27;
    <                    &lt;                       &#60;                        &#x3C;
    >                    &gt;                       &#62;                        &#x3E;

PCDATA vs CDATA

#PCDATA

  • parsed character data
  • In simple understanding, the parser will parse text in tag and it should contain predefined entities of reserved character if any passed.
  • <!ELEMENT <elementName> (#PCDATA)>

#CDATA

  • character data
  • In simple understanding, the parser will not parse text in tag and either that text contains predefined entities or reserved character, it will not occurs any error.

i.e.

//Valid
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE list [
<!ELEMENT list (song)>
<!ELEMENT song (singer-name)+>
<!ELEMENT singer-name (#PCDATA)>
]>
<list>
  <song>
    <singer-name>
     Ike - Tina Turner
     <![CDATA[ River Deep & Mountain High ]]>
    </singer-name> 
  </song>
</list>
//Invalid
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE list [
<!ELEMENT list (song)>
<!ELEMENT song (singer-name)+>
<!ELEMENT singer-name (#PCDATA)>
]>
<list>
  <song>
    <singer-name>
     Ike & Tina Turner
     <![CDATA[ River Deep & Mountain High ]]>
    </singer-name> 
  </song>
</list>

Reading XML file in NodeJS

Steps:

  • mkdir xml-example
  • cd xml-example
  • npm init -y
    • This will generate a package.json file with a yes option for all question
  • npm i express cors nodemon
    • cors helps to access our project in HTML
    • nodemon helps automatically restart on change
  • make sample JSON data file,
    • XML data will prepare with the above file data

songs.json

[
        {
            "title": "Like a Rolling Stone",
            "artist": "Bob Dylan",
            "year": "1965"
        },
        {
            "title": "(I Can't Get No) Satisfaction",
            "artist": "The Rolling Stones",
            "year": "1965"
        },
        {
            "title": "Imagine",
            "artist": "John Lennon",
            "year": "1971"
        },
        {
            "title": "What's Going On",
            "artist": "Marvin Gaye",
            "year": "1971"
        },
        {
            "title": "Respect",
            "artist": "Aretha Franklin",
            "year": "1967"
        },
        {
            "title": "Good Vibrations",
            "artist": "The Beach Boys",
            "year": "1966"
        },
        {
            "title": "Johnny B. Goode",
            "artist": "Chuck Berry",
            "album": "The Anthology",
            "year": "1958"
        },
        {
            "title": "Hey Jude",
            "artist": "The Beatles",
            "album": "Hey Jude",
            "year": "1968"
        },
        {
            "title": "Smells Like Teen Spirit",
            "artist": "Nirvana",
            "album": "Nevermind",
            "year": "1991"
        },
        {
            "title": "What'd I Say",
            "artist": "Ray Charles",
            "album": "What'd I Say",
            "year": "1959"
        }
]
  • make an index file named index.js
const list = require("./songs.json");      //importing sample json data file
const express = require("express");
const cors = require('cors');

const app = express();
app.use(cors({ origin: '*' }));

app.get("/", (req, res, next) => {

//xml related code 
  let data = `<?xml version="1.0" encoding="UTF-8"?>`;

  data += `<list>`;
  for (let i = 0; i <= list.length; i++) {
    data += `<song> 
       <title><![CDATA[ ${list[i]['title']} ]]></title>
       <artist><![CDATA[ "${list[i]['artist']}" ]]></artist>
       <year><![CDATA[ ${list[i]['year']} ]]></year>
    </song>`;
  }
  data += `</list>`;

  res.header("Content-Type", "application/xml");
  res.status(200).send(data);
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
  • nodemon index.js
    • this will run our application

Now, let's test our NodeJS application

  • Go to http://localhost:3000/

image.png

XML request and response

  • To show response data from the above API just change URL parameter of xhttp.open in HTML file

    <html>
    <head>
      <style>
          .title,
          .artist {
              margin: auto 0%;
              text-align: center;
              vertical-align: middle;
              font-family: system-ui;
          }
          .title {
              font-weight: 300;
              margin-bottom: 5px;
          }
          .artist {
              color: #04bfbf;
          }
          .year {
              position: absolute;
              top: 98px;
              left: 11px;
              transform: rotate(14deg);
              font-size: 26px;
              height: 450px;
              font-family: system-ui;
          }
          .block {
              position: relative;
              margin: 5px;
              padding: 5px;
              border: 1px solid black;
          }
          table {
              width: 100%;
              text-align: center;
          }
          table tr td {
              display: inline-block;
              width: 224px;
              height: 265px;
          }
          .head {
              font-size: 31px;
              text-align: center;
              font-family: system-ui;
              font-weight: 400;
          }
          .head span {
              border-bottom: 4px solid #04bfbf;
          }
      </style>
    </head>
    <body>
      <div>
          <h3 class="head"><span>Song lists</span></h3>
          <table id="song-list">
          </table>
      </div>
      <script>
          var xhttp = new XMLHttpRequest();
          xhttp.onreadystatechange = function () {
              if (this.readyState == 4 && this.status == 200) {
                  displayList(this);
              }
          };
          xhttp.open("GET", "http://localhost:3000/", true);
          xhttp.send();
    
          function displayList(xml) {
              var xmlDoc = xml.responseXML;
              var song = xmlDoc.getElementsByTagName("song");
              var table = '<tr>';
              for (i = 0; i < song.length; i++) {
                  table += "<td><div class='block'>" +
                      "<img src='./music.gif' width=200 class='poster'>" +
                      "<p class='year'>" + song[i].getElementsByTagName("year")[0]
                          .childNodes[0].nodeValue + "</p>" +
                      "<p class='title'>" + song[i].getElementsByTagName("title")[0]
                          .childNodes[0].nodeValue +
                      "</p><p class='artist'>" + song[i].getElementsByTagName("artist")[0]
                          .childNodes[0].nodeValue + "</p></div></td>"
              }
              table += '</tr>';
              document.getElementById("song-list").innerHTML = table;
          }
      </script>
    </body>
    </html>
    
  • Now let's make an API which returns data from the database and also below example covers reading XML requests.

index.js

const express = require("express");
const cors = require('cors');
var xmlparser = require('express-xml-bodyparser');

const app = express();
app.use(cors({ origin: '*' }));
app.use(xmlparser());

app.post("/from-db", async function (req, res, next) {
  try {
    console.log(req);
    const pagination = req?.body?.pagination
    const took = parseInt(pagination?.took)|0;
    const skip = parseInt(pagination?.skip)|0;

    console.log('took:'+took, 'skip:'+skip);
  } catch (error) {
    res.send({ error: error.message || error.toString() });
  }
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
  • To read xml request you need install, npm i express-xml-bodyparser which helps to read incoming xml request is similar to bodyparser(JSON),
  • Run the application and check the terminal log, might look like a below-attached snapshot

image.png

  • Now let's connect with the database and retrieve data from the connected database and send it in XML format.

    You can also go with xml-schema as MySQL itself allow returns query result in xml format.

  • Firstly you need to install MySQL package npm i mysql and make common file for executing all queries.

SQL queries:

CREATE TABLE `song` (
  `id` int(11) NOT NULL,
  `title` varchar(500) NOT NULL,
  `artist` varchar(500) NOT NULL,
  `year` varchar(500) NOT NULL
)

INSERT INTO `song` (`title`, `artist`, `year`) VALUES
('Like a Rolling Stone', 'Bob Dylan', '1965'),
('(I Can\'t Get No) Satisfaction', 'The Rolling Stones', '1965'),
('Imagine', 'John Lennon', '1971'),
('What\'s Going On', 'Marvin Gaye', '1971'),
('Respect', 'Aretha Franklin', '1967'),
('Good Vibrations', 'The Beach Boys', '1966'),
('Johnny B. Goode', 'Chuck Berry', '1958'),
('Hey Jude', 'The Beatles', '1968'),
('Smells Like Teen Spirit', 'Nirvana', '1991'),
('What\'d I Say', 'Ray Charles', '1959'),
('My Generation', 'The Who', '1965'),
('A Change Is Gonna Come', 'Sam Cooke', '1964'),
('Yesterday', 'The Beatles', '1965'),
('Blowin\' in the Wind', 'Bob Dylan', '1963'),
('London Calling', 'The Clash', '1980'),
('I Want to Hold Your Hand', 'The Beatles', '1963'),
('Purple Haze', 'The Jimi Hendrix Experience', '1967'),
('Maybellene', 'Chuck Berry', '1955'),
('Hound Dog', 'Elvis Presley', '1956'),
('Let It Be', 'The Beatles', '1970');

/config/database.js

const mysql = require("mysql");

const connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
    database: "xml-demo",
})

connection.connect((err) => {
    if (err) {
        throw err;
    }
    console.log("MySql Connected");
});
function executeQuery(query, value = '') {
    return new Promise(function (resolve, reject) {
        console.log('executeQuery');
        connection.query(query, value, (error, results) => {
            console.log(error,query);
            (error) ? reject(error) : resolve(results);
        })
    })
}
module.exports=  {  connection  , executeQuery }
  • Now import the above file to index.js and make a query
const list = require("./songs.json");
const express = require("express");
const cors = require('cors');
var xmlparser = require('express-xml-bodyparser');
const { executeQuery } = require('./config/database');

const app = express();
app.use(cors({ origin: '*' }));
app.use(xmlparser());

app.post("/from-db", async function (req, res, next) {
  try {
    const pagination = req?.body?.pagination
    const took = parseInt(pagination?.took)|0;
    const skip = parseInt(pagination?.skip)|0;

    let sql = `SELECT * FROM song LIMIT ${skip},${took}`;
    const results = await executeQuery(sql);
    let xmlData = `<?xml version="1.0" encoding="UTF-8"?>`;
    xmlData += `<list>`;
    if (results && results.length > 0) {
      for (let i = 0; i <= results.length; i++) {
        xmlData += `<song> 
           <title><![CDATA[ ${list[i]['title']} ]]></title>
           <artist><![CDATA[ "${list[i]['artist']}" ]]></artist>
           <year><![CDATA[ ${list[i]['year']} ]]></year>
        </song>`;
      }
    };
    xmlData += `</list>`;
    res.send(xmlData)

  } catch (error) {
    res.send({ error: error.message || error.toString() });
  }
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
  • Check API working or not in postman,

image.png

0
Subscribe to my newsletter

Read articles from Kajal Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kajal Singh
Kajal Singh