Ajax, Web Services & XML Part II
Last time we created an Ajax application that wrapped an XML request in a SOAP envelope and called a web service. The web service returned another SOAP stream with a listing of theaters, movies and show times. We settled for showing the data in a textarea. In this installment we’ll go the final yard and parse the XML into tabular data to be displayed in the browser.
If you look at file ajax2b.html you’ll see that the “updateMe()” method has been updated to call the ActiveXObject “Microsoft.XMLDOM”. Obviously, this method works only for Internet Explorer. There are other methods that work in Netscape and other browsers.
XMLDOM is a Document Object Model. It will parse the XML into Nodes that can be traversed and converted into other forms. There are other tools available to us for this type of work. These include XSLT and XPATH. Today, we’ll use the DOM and write a “recursive descent parser”.
The XML is converted into a DOM Node tree by calling the xmlDoc.loadXML() method. We pass in the request.responseText which is the complete text of the XML returned by the web service. (There is also a request.responseXML property, but does not work consistently between browsers.) Now that we have a valid DOM tree, we can traverse the nodes and create our HTML.
The workhorse of the parser is the “depth first search” method or “dfs”. This is a recursive method that descends into the XML tree looking for the “Theater” node. Once it finds one, it calls the “theater” method to further parse the tree and append to an HTML string.
“Theater()” iterates across its children looking for the “Name” (the name of the theater”, “Address” (theater address), and “Movies” nodes. The Name and Address nodes become headers (<h1> & <h2>). The Movies node is passed to the “movies” method which iterates across its children looking for “Movie” nodes. Every “Movie” node is passed to the “movie” method.
The “movie” method creates a <table> of movie entries. The “Rating”, “Name”, “RunningTime”, and “ShowTimes” comprise the row of a table showing what is playing at the current theater.
Once the entire tree is converted to HTML, we need to display it. This is accomplished by creating a
Document.getElementById(“result”).innerHTML = html.
This replaces the
This technique has one minor drawback. It makes debugging the page difficult. When you pull down the “View Source” menu item, you get the original source code. Updating the document using “innerHTML” changes the internal representation without changing the HTML that creates the page.
In this article we’ve seen how to receive an XML/SOAP package and parse it into a Document Object Model tree. We traversed the tree and converted it into HTML, and finally updated the document with HTML using the innerHTML property. These methods give Ajax the power to dynamically update a page without receiving a new response from the web server and elevate the Javascript/browser into the realm of Rich Client.
Functions Used:
function makeCall(url, callback, xml) {
request = new ActiveXObject("Microsoft.XMLHTTP");
if (request) {
var now = new Date();
request.onreadystatechange = callback;
request.open("POST", url, true);
request.setRequestHeader("SOAPAction", "<a href="http://www.ignyte.com/whatsshowing/GetTheatersAndMovies">http://www.ignyte.com/whatsshowing/GetTheatersAndMovies</a>");
request.setRequestHeader("User-Agent", "Mindreef SOAPscope 4.1.2000 (<a href="http://www.mindreef.com/">http://www.mindreef.com</a>")
request.setRequestHeader("Content-Type", "text/xml; charset=UTF-8");
request.send(xml);
}
}
function theaters() {
var zip = document.getElementById("zip").value;
var radius = document.getElementById("radius").value;
xml = '<?xml version="1.0" encoding="UTF-8"?>';
xml += '<soap:Envelope ';
xml += 'xmlns:soap="<a href="http://schemas.xmlsoap.org/soap/envelope/">http://schemas.xmlsoap.org/soap/envelope/</a>" ';
xml += 'xmlns:tns="<a href="http://www.ignyte.com/whatsshowing">http://www.ignyte.com/whatsshowing</a>" ';
xml += 'xmlns:xs="<a href="http://www.w3.org/2001/XMLSchema">'">http://www.w3.org/2001/XMLSchema">'</a>;
xml += ' <soap:Body>';
xml += ' <tns:GetTheatersAndMovies>';
xml += ' <tns:zipCode>'+zip+'</tns:zipCode>';
xml += ' <tns:radius>'+radius+'</tns:radius>';
xml += ' </tns:GetTheatersAndMovies>';
xml += ' </soap:Body>';
xml += '</soap:Envelope>';
document.getElementById("request").value = xml;
makeCall('http://www.ignyte.com/webservices/ignyte.whatsshowing.webservice/moviefunctions.asmx', updateMe, xml);
}
function updateMe() {
if (request.readyState != 4) return;
if (request.status != 200) return;
html = "";
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.loadXML(request.responseText);
dfs(xmlDoc);
document.getElementById("request").value = request.responseText;
document.getElementById("result").innerHTML = html;
request.abort();
request = null;
}
function dfs(node) {
var stuff = "";
for(var i=0; i<node.childNodes.length; i++) {
var thisNode = node.childNodes(i);
if (thisNode.nodeName == "Theater") {
html += theater(thisNode) + "n";
} else {
dfs(thisNode);
}
}
return stuff;
}
function theater(node) {
var stuff = "";
for(var i=0; i<node.childNodes.length; i++) {
var thisNode = node.childNodes(i);
if (thisNode.nodeName == "Name") {
stuff += "<h1>"+thisNode.childNodes(0).nodeValue+"</h1>n";
} else if (thisNode.nodeName == "Address") {
stuff += "<h2>"+thisNode.childNodes(0).nodeValue+"</h2>n";
} else if (thisNode.nodeName == "Movies") {
stuff += movies(thisNode);
}
}
return stuff;
}
function movies(node) {
var stuff = "<table border=1>";
for(var i=0; i<node.childNodes.length; i++) {
var thisNode = node.childNodes(i);
if (thisNode.nodeName == "Movie") {
stuff += "<tr>"+movie(thisNode)+"</tr>";
}
}
return stuff+"</table>";
}
function movie(node) {
var stuff = "";
for(var i=0; i<node.childNodes.length; i++) {
var thisNode = node.childNodes(i);
if (thisNode.nodeName == "Rating") {
stuff += "<td>"+thisNode.childNodes(0).nodeValue+"</td>";
} else if (thisNode.nodeName == "Name") {
stuff += "<td>"+thisNode.childNodes(0).nodeValue+"</td>";
} else if (thisNode.nodeName == "RunningTime") {
stuff += "<td>"+thisNode.childNodes(0).nodeValue+"</td>";
} else if (thisNode.nodeName == "ShowTimes") {
stuff += "<td>"+thisNode.childNodes(0).nodeValue+"</td>";
}
}
return stuff;
}
Author: Greg Smith