Saturday 11 July 2009

jQuery - Copy XML from one object to another

Today I am going to go through, how to copy some XML structure and data from one XML object to another XML object. Now from what I have noticed when making use of jQuery, I have seen that it does not actually support XML as such, while it does support XHTML formatted in an XML structure. So in essense, we will be converting our XML object into an XHTML object, do all of our changes, and then convert it back. This may sound a bit long winded, but it's actually not as bad as it sounds.

First off, we need to add a function created by John Resig (http://ejohn.org/blog/javascript-array-remove/), which will allow us to remove items from an array.

Array.prototype.remove = function(fr, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};



And now we need to extend our XML to string function, as in good XML you tend to get the first line starting with a "
jQuery.XMLtoString = function(xmlData) {
var xData;
var xSplit;
if (window.ActiveXObject) {
xData = xmlData.xml;
} else {
xData = (new XMLSerializer()).serializeToString(xmlData);
}
xSplit = xData.split('\r\n');
if (xSplit[0].substr(0, 5) == ' xSplit.remove(0);
return xSplit.join('\r\n');
}



Now I have only just thrown this together, it works, however I will be making some changes to it at a later date by making use of some Regex, as well as giving you the ability to re-add the line you are removing. Anyway, let's give an example XML structure:








Scenario:

You want to copy all of the address tags, including all attributes and child nodes, onto a new location on a different XML object. Resulting in something like this:



45 Usura Place
Hailey
ID


3 Prufrock Lane
Stamford
CT


10 Bridge Tunnel
Harlem
NY



This is a fairly easy task, however the way you go about doing it, is not very obvious. Let's say you have done your AJAX request, received your XML, and have now passed the xml to your function. Here's what you should have inside your function.

Firstly, we need to convert our XML into a string (with the modified function), find our "address" tags, and convert the string into an object. You can do that all in a single line:

var xData = $($.XMLtoString(xml)).find("address");


If you are not already aware of this, encapsulating a string with $(), converts it to an object (with jQuery).

Now we have all the data we want from the XML document, but we need to create our new XML document to contain the data within. Now for some reason (which I am yet to investigate), the first element in a newly created object is removed upon creation, so we need to keep this in mind when creating it. We want a root of "", so we need to add a dummy node above that so that our one is not removed. Our code will look like this:

var xDoc = $("");


Next we need to specify where inside this new document, to inject our original data, which is done with a simple find() like so:

xDoc.find("locations").append(xData);


That's it, our data has been added to the new object. You can print it out to a textarea to view the results by calling the .html() function on the new document, so something like this:

$("#output").text(xDoc.html());


Here is what the resulting function would look like:

function parseXml(xml) {
var xData = $($.XMLtoString(xml)).find("address");
var xDoc = $("");
xDoc.find("locations").append(xData);
$("#output").text(xDoc.html());
}



As I said earlier, it's not very obvious, but when you know what to do, it's actually quite an easy task.

3 comments:

Anonymous said...

Hi Justin,

I would like to thank you for this post.

Having a JQUERY blog to talk about manipulating XML with JQUERY was a fantastic idea. There is a gap on the NET for that and I have to congratulate you for coming up with this blog.

I have some questions regarding the functions you showed above - the array function and JQuery.XmlToString function.

I assume that I have to create a new .js file, add both functions and then include this file to the html page after the reference to the JQuery library. Is my assumption right?

Also, could you please explain the code where you split the array using \r\n and then you use

substr(0,5) == '

What is it doing?

Why are you joining it back again?

Cheers,

C

Unknown said...

Thanks for the comment, and sorry for the lack of clarification in the areas you mentioned. Let me go through them with you here:

The new functions you can either include in an external js file, and then include the files within the document you need to use them, or you could just add them inside of a regular script tag. Now any functions starting with "jQuery.", require jQuery initialized to operate, so you can include them where-ever you wish, however you cannot make use of them until the page has completed loading, so the call is generally made within a tag like this:

$(document).ready(function() {
$.jQueryFunction();
});

I will try attach a demonstration page a bit later, showing how it should/could look.


As for the array, I wanted to analyse the first line in the string (xml converted to string), and if it began with a "?xml" tag, I wanted to remove the entire line. The quickest way for me to do that was to split the string into an array, spliting by the line breaks (\r\n). Then analyzing the first item in the array (first line in the string), checking if the first 5 characters (substr(0,5)) matched the start of a "?xml" tag, and removing it if if did. Afterwards I then rejoining the string after my manipulation was complete. Now I plan to replace this with some Regex, but this was just quicker for me, and got the point of what I was trying to do across.

Ultimately, I plan to write a class and set of functions to make it much easier to manipulate XML, but first I need to find out more about what functionality people would need. As and when I find new functionality, I will add the code snippets onto this blog, and slowly copy it into my class which I would release after I am satisfied with it.

Anonymous said...

Hi Justin,

Thanks for your clarificaion regarding the array function.

Wouldn't be easier to just use a reg expression to remove the namsespace? Something like this:

tempDoc = tempDoc.replace(/ xmlns=\"\"/ig,"")

Also, you said the blogger has removed some code from the snippets you provided.

could you please let us know what changes do we need to make in the snippets so that we can try the code locally

I have a lot of ideas about the functionality that people would like since I work a lot with XML in my development. I will make a list of my ideas and post that to you.

Cheers

C