cmarguel (cmarguel) wrote,

Transform XML into a Javascript object

After the Wordpress editor crashed all three of my browsers (Firefox, IE, Opera), I decided to say "screw it" and post here instead of the blog I made for the stuff that's boring to the public at large.

I’ve been looking for a Javascript way to navigate through an XML document like an object. That is, I wanted to take an XML document returned from an AJAX request (eg:
<book>
  <author>
     <name>Miguel</name>
  </author>
  <title>Hello</title>
</book>
), and go through it by saying something like “book.author.name”. Surprisingly, it was very difficult to find a solution–or more accurately, impossible. I had figured that this was something someone would have invented long ago, but I actually couldn't any implementations of this idea. I ended up writing my own function, and I’ve posted it below. (I realize that I could have saved effort by using JSON instead, but XML I don't think you'll always be able to get a JSON representation for a project)

I get the impression that this function is going to be slow–I put each node into an array (in case there are multiple nodes with the same name), and afterwards, flatten any arrays of length 1. However, I don't think it's slow enough to have any visible effect on small files (you have no business doing it with big files anyhow!). Any comments are welcome, as this code probably has shortcomings. For starters, it will flatten any array of length 1, even if it makes more sense to keep it as an array (eg: a grocery list consisting of one item should still be an array). Also, this code gets attributes as well, so be sure not to have any conflicting names. I'm sure there I made other unsafe assumptions, but for now, it works on Firefox, Opera, and IE.
    function createObj(xml) {
var obj = {};
if(xml.attributes) {
for(var j = 0; j < xml.attributes.length; j++) {
obj[xml.attributes[j].name] = xml.attributes[j].value;
}
}
for(var i = 0; i < xml.childNodes.length; i++) {
var x = xml.childNodes[i];
/* Note: IE and Opera do not define Node or its constants. You'll need to define them yourself
* for the benefit of said browsers.
*
* Meanwhile, the childNodes.length check is there because Firefox counts any whitespace as text nodes.
* This function, then, will only work on the assumption that "real" text nodes are the lone content
* of their parents.
*/
if ((x.nodeType == Node.TEXT_NODE && xml.childNodes.length == 1)) {
return x.nodeValue;
}
if (x.nodeType == Node.ELEMENT_NODE) {
// Assume a child node consisting of the empty string if there are no further children.
if (x.childNodes.length == 0) {
return "";
}
if (!obj[x.nodeName]) {
obj[x.nodeName] = $A();
}
obj[x.nodeName][obj[x.nodeName].length] = createObj(x);
}
}

for(var a in obj) {
if(obj[a] instanceof Array && obj[a].length == 1) {
obj[a] = obj[a][0];
}
}
return obj;
}
  • Post a new comment

    Error

    default userpic
  • 3 comments