Using JQuery and Javascript to parse XML to objects
Right – thought I’d post some actual code as proof of actual development.
I wrote the first version of my CMS back-end in PHP, and sent data to a Javascript front-end using JSON. This was fantastically fast and simple – the string only need be checked for dangerous syntax, and then eval-ed and it becomes Javascript objects ready to use. The pot-noodle of data formats.
Pot noodles are all very well but my friend Alex prefers XML. It’s much more readable, and pretty much every language and its dog can parse it. I wrote a function to turn my Python data into XML which I’ll post later, but this post is about my Javascript XML parser, as it was harder to get right.
In summary the parser function takes a complex heirarchical XML tree with nodes and attributes, and turns it into equivalent JS objects and properties. The full code as it stands is at the end of the post, I’ll go through the main steps here:
- The function accepts the first child of the XML file and a parent object as arguments.
- If the node name ends with ‘_array’ then it is, unsurprisingly, an array. This is a naming convention, but I couldn’t see how else to make sure arrays were consistently dealt with.
- If the node has no attributes of its own, treat it as an attribute itself. Again, this is a weird convention on my part, so I can use nodes to store large sections of text, instead of putting them in XML attributes, which would be hard to read.
- Check whether the object can be pushed into a parent array, if that fails the parent must be an object – append the the new object to the parent.
- Loop the attributes and assign them as properties to the object.
- Loop the child nodes and send them to the function with the current array/object as parent.
Javascript code:
xmlToObj : function(xml,parent_obj) {
if (parent_obj == null)
{
// base object
var new_obj = {};
xml = xml.firstChild;
} else if (xml.nodeName.search(/_array$/) != -1) {
// array
var new_obj = parent_obj[xml.nodeName] = [];
} else if(xml.attributes.length == 0) {
// node which is really a string attribute
try {
parent_obj[xml.nodeName] = xml.firstChild.nodeValue;
} catch(error) {
parent_obj[xml.nodeName] = '';
}
} else {
if (parent_obj instanceof Array) {
//member of an array
var new_obj = {};
parent_obj[$(xml).attr('id')] = new_obj;
} else {
// new object
var new_obj = parent_obj[xml.nodeName] = {};
}
}
// map xml attributes to object properties
$(xml.attributes).each(function()
{
if (this.nodeValue.toLowerCase() == 'true') {
new_obj[this.nodeName] = true;
} else if (this.nodeValue.toLowerCase() == 'false') {
new_obj[this.nodeName] = false;
} else {
new_obj[this.nodeName] = parseInt(this.nodeValue);
}
});
if ($(xml).children().length > 0)
{
$(xml).children().each(function()
{
page_edit.xmlToObj(this, new_obj);
});
}
return new_obj;
}
XML data
<page active="True" id="1" is_template="False" position="1" template_id="1" visible="True">
<title>
project
</title>
<sections_array>
<page_section id="1" page_id="1" position="0" section_id="1" visible="True">
<section active="True" attribs="None" id="1">
<elements_array>
<section_element element_id="1" id="1" position="0" section_id="1" visible="True">
<element active="True" attribs="None" id="1">
<content>
test
</content>
<title>
title
</title>
</element>
</section_element>
<section_element element_id="2" id="2" position="1" section_id="1" visible="True">
<element active="True" attribs="None" content="None" id="2">
<title>
test
</title>
</element>
</section_element>
</elements_array>
<title>
test_section
</title>
</section>
</page_section>
<page_section id="2" page_id="1" position="1" section_id="2" visible="True">
<section active="True" attribs="None" id="2">
<elements_array>
<section_element element_id="4" id="4" position="0" section_id="2" visible="True">
<element active="None" attribs="None" id="4">
<content>
Hello my name is Joe
</content>
<title>
Fred
</title>
</element>
</section_element>
<section_element element_id="3" id="3" position="1" section_id="2" visible="True">
<element active="None" attribs="None" id="3">
<content>
some content
</content>
<title>
Feedy doo da
</title>
</element>
</section_element>
</elements_array>
<title>
arab
</title>
</section>
</page_section>
</sections_array>
</page>
