Categories:

Retrieving an XML document using Ajax

When making a server request in Ajax, the data returned can be in either plain text/html, or an XML document instead. The later is technically just a text file as well, but with some special instructions, Ajax can retrieve that well formed XML text file and return it back to you as a XML object. This enables the XML data to be easily parsed using standard DOM methods.

Here's a simple XML document in RSS format I'll be using for illustration (lets name it "javascriptkit.xml"):

<?xml version="1.0" encoding="ISO-8859-1"?>

<rss version="0.91">
<channel>
<title>JavaScriptKit.com</title>
<link>http://www.javascriptkit.com</link>
<description>JavaScript tutorials and over 400+ free scripts!</description>
<language>en</language>

<item>
<title>Document Text Resizer</title>
<link>http://www.javascriptkit.com/script/script2/doctextresizer.shtml</link>
<description>This script adds the ability for your users to toggle your webpage's font size, with persistent cookies then used to remember the setting</description>
</item>

<item>
<title>JavaScript Reference- Keyboard/ Mouse Buttons Events</title>
<link>http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml</link>
<description>The latest update to our JS Reference takes a hard look at keyboard and mouse button events in JavaScript, including the unicode value of each key.</description>
</item>

<item>
<title>Dynamically loading an external JavaScript or CSS file</title>
<link>http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml</link>
<description>External JavaScript or CSS files do not always have to be synchronously loaded as part of the page, but dynamically as well. In this tutorial, see how.</description>
</item>

</channel>
</rss>

The below shows retrieving this XML document and outputing the headlines ("title" elements) of each entry:

<div id="result"> </div>

<script type="text/javascript">

function ajaxRequest(){
 var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] //activeX versions to check for in IE
 if (window.ActiveXObject){ //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken)
  for (var i=0; i<activexmodes.length; i++){
   try{
    return new ActiveXObject(activexmodes[i])
   }
   catch(e){
    //suppress error
   }
  }
 }
 else if (window.XMLHttpRequest) // if Mozilla, Safari etc
  return new XMLHttpRequest()
 else
  return false
}


var mygetrequest=new ajaxRequest()
if (mygetrequest.overrideMimeType)
 mygetrequest.overrideMimeType('text/xml')

mygetrequest.onreadystatechange=function(){
 if (mygetrequest.readyState==4){
  if (mygetrequest.status==200 || window.location.href.indexOf("http")==-1){
   var xmldata=mygetrequest.responseXML //retrieve result as an XML object
   var rssentries=xmldata.getElementsByTagName("item")
   var output='<ul>'
   for (var i=0; i<rssentries.length; i++){
    output+='<li>'
    output+='<a href="'+rssentries[i].getElementsByTagName('link')[0].firstChild.nodeValue+'">'
    output+=rssentries[i].getElementsByTagName('title')[0].firstChild.nodeValue+'</a>'
    output+='</li>'
   }
   output+='</ul>'
   document.getElementById("result").innerHTML=output
  }
  else{
   alert("An error has occured making the request")
  }
 }
}

mygetrequest.open("GET", "javascriptkit.xml", true)
mygetrequest.send(null)

</script>

Demo:

The key points to remember when getting a XML document using Ajax are:

  • Detect for and call the method request.overrideMimeType('text/xml') before sending the request. This is a necessary step to address an issue in certain versions of Firefox. IE has much bigger problems- see next section below.
  • When accessing the returned data, call request.responseXML instead of responseText to get the data as an XML object (assuming the data is a valid XML file).

The data returned by request.responseXML is an XML object that can be parsed using standard DOM methods and properties. Notice that to access the actual contents of an element, you need to drill down all the way to the element's "firstChild" object before looking up its "nodeValue".

XML documents and the "Content-type" pitfall in IE

There is a major pitfall to be aware of when getting XML documents via Ajax in IE (as of IE7). Not knowing this will have you pulling your hair out wondering why you can't work on the returned data as an XML object. So what's this giant hole in the ground? Well, in IE, XML documents must return an explicit "content-type" header corresponding to XML (ie: "Content-type: text/xml") in order for the browser to know that this is an XML document. Without this header, the file will be retrieved as a regular text file instead of turning its contents into an XML object. Firefox has this requirement as well actually, though instead of solely relying on the server to hopefully generate the correct "content-type" header for a given file, it supports the client side method "request.overrideMimeType()" that you can use to just tell the browser manually and unequivocally what the type of the target file is. That's why we call the lines:

if (mygetrequest.overrideMimeType)
 mygetrequest.overrideMimeType('text/xml')

in the code earlier in Firefox, so FF is told manually that the target file is an XML one. Any question, either coming from the server end or the browser's with regards to what the heck the type of the target file is instantly silenced by calling this method, no problem. However, IE the rebel doesn't support "request.overrideMimeType()", which means you must rely fully on your server- and tweak it if necessary- to properly generate the correct "content-type" headers for XML documents. No proper header, and the file will be returned as plain text, instead of an XML object that you access using "request.responseXML".

Typically a server is already set up to generate the proper "content-type" headers for the well known file extensions, such as "text/xml" for all files with an ".xml" extension. Assuming this is the case on your server and the file you're retrieving via Ajax is called "sample.xml", then you won't run into any problems in IE, as the correct XML header has been added. But consider all the other possible situations in which you would:

  1. Your server isn't set up to recognize ".xml" files as XML documents.
  2. You named the XML file with a custom extension such as "sample.rss"
  3. Your XML file is dynamically generated via a server side script, so its extension may be "sample.php"

For 1) and 2), the solution is to modify your server's configuration to output a correct XML content mimetype based on the file extensions that should be recognized as XML documents. On Unix/Linux servers for example, this can usually be done in one of two ways:

1) Add to the ".htaccess" file that exists in the HTML root directory of your site the line (more info):

AddType application/xml .xml .rss

2) OR, at an even lower level, add the above line to the "httpd.config" file of your server instead (look under the "mimetypes" section). Alternatively, if your server uses a graphical control panel such as cPanel, you can just click on the "Mime Types" button on the admin frontpage to make the changes.

For 3), the solution is simpler. If your XML file is dynamically generated by a server side script, simply ensure that the script outputs an XML header before the actual XML contents. Using a PHP script for example, you'd do:

<?
header('Content-type: text/xml');

echo "Actual XML contents...";

?>

Yes, curse the day IE was born!