Skip to main content

XML processing in Ajax, Part 2: Two Ajax and XSLT approaches

Transforming XML in Ajax with XSLT

Mark Pruett (mark.l.pruett@dom.com), System Architect, Dominion
Mark Pruett is a Systems Architect at Dominion. He's a contributing author to O'Reilly Media's book Ajax Hacks and author of the O'Reilly Shortcuts Ajax and Web Services and Yahoo! Pipes.

Summary:  In Part 2 of this series, Mark Pruett presents two more approaches to the Asynchronous JavaScript + XML (Ajax) weather badge. Both approaches use Extensible Stylesheet Language Transformation (XSLT) transformations—one on the server side and the other in the browser.

View more content in this series

Date:  11 Mar 2008
Level:  Intermediate
Activity:  4473 views

Part 1 of this series introduced a problem specification: to build a weather badge that can be inserted easily into any Web page. The weather badge is constructed using Ajax techniques and uses data provided by the United States National Weather Service (NWS). That NWS data is provided in an XML format, updated every 15 minutes.

This series looks at four separate approaches for implementing the weather badge. In the first installment, the first approach used an Apache Web server rule to proxy the NWS XML data into the browser. Then, with JavaScript code, it extracted the required data from the DOM, reformatted it into HTML, and displayed it.

This installment looks at the second and third approaches. These two approaches share one thing in common: they both use XSLT.

XSLT

Frequently used acronyms

  • DOM: Document Object Model
  • HTML: Hypertext Markup Language
  • XML: Extensible Markup Language
  • XSLT: Extensible Stylesheet Language Transformation

XSLT is a language to query XML and transform XML into other formats. This is precisely the problem I have with my weather data—it's packaged as XML, but I want something more user- (and browser-) friendly. And the NWS data contains a lot more information than the weather badge requires. Some technique is required to extract just the data items I need. XSLT can handle both these requirements.

It's beyond the scope of this article to provide an XSLT tutorial. To find out more about XSLT, see the developerWorks article "What kind of language is XSLT? (see Resources).

The XSLT language differs from many other computer languages in that its syntax is valid XML. This can be a trifle confusing if you're used to the C, Java™, Perl, or Python languages.

Because both my second and third approaches to the weather badge use the same XSLT, I'll introduce and explain it first. Later I'll show how the XSLT piece fits into the overall solutions.

Using XSLT to transform data

First, review the NWS XML data format. Listing 1 shows an abbreviated example.


Listing 1. Sample NWS XML data file KNGU.xml (abridged)
                
<?xml version="1.0" encoding="ISO-8859-1"?> 
<current_observation version="1.0"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation=
   "http://www.weather.gov/data/current_obs/current_observation.xsd">
  <credit>NOAA's National Weather Service</credit>
  <credit_URL>http://weather.gov/</credit_URL>
  <image>
    <url>http://weather.gov/images/xml_logo.gif</url>
    <title>NOAA's National Weather Service</title>
    <link>http://weather.gov</link>
  </image>
  <suggested_pickup>15 minutes after the hour</suggested_pickup>
  <suggested_pickup_period>60</suggested_pickup_period>
  <location>Norfolk, Naval Air Station, VA</location>
  <station_id>KNGU</station_id>
  <latitude>36.94</latitude>
  <longitude>-76.28</longitude>
  <observation_time>
                Last Updated on Jan 7, 2:53 pm EST
                </observation_time>
  <observation_time_rfc822>
    Mon,  7 Jan 2008 14:53:00 -0500 EST
  </observation_time_rfc822>
  <weather>Fair</weather>
                <temperature_string>74 F (23 C)</temperature_string>
  <temp_f>74</temp_f>
  <temp_c>23</temp_c>
  <relative_humidity>34</relative_humidity>
                <wind_string>From the Southwest at 9 Gusting to 18 MPH</wind_string>
  <wind_dir>Southwest</wind_dir>
  <wind_degrees>240</wind_degrees>
  <visibility_mi>10.00</visibility_mi>
                <icon_url_base>
                http://weather.gov/weather/images/fcicons/
                </icon_url_base>
                <icon_url_name>
                skc.jpg
                </icon_url_name>
  <disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
  <copyright_url>http://weather.gov/disclaimer.html</copyright_url>
  <privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
</current_observation>

I'm only interested in the data highlighted in Listing 1, so the first job of my XSLT program is to extract those desired elements. The second job is to reformat that subset of data into HTML for display in the browser.

Listing 2 shows the XSLT program used to accomplish those two goals.


Listing 2. weather2html.xsl, the weather data XSLT program
                
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" />

  <xsl:template match="/current_observation">

    <center>
    <b><xsl:value-of select="location" /></b><br/>
    <xsl:value-of select="weather" /><br/>

    <xsl:variable name="icon_url_base" select="icon_url_base"/>
    <xsl:variable name="icon_url_name" select="icon_url_name"/>

    <img border='0' src='{$icon_url_base}{$icon_url_name}'/><br/>
    <xsl:value-of select="temperature_string" /><br/>
    Wind: <xsl:value-of select="wind_string" /><br/>
    Humidity: <xsl:value-of select="relative_humidity" />%<br/>
    Visibility: <xsl:value-of select="visibility_mi" /> miles<br/>
    <br/><span style='font-size: 0.8em; font-weight: bold;'>
    <xsl:value-of select="observation_time" /></span><br/>

    </center>

  </xsl:template>

</xsl:stylesheet>

First, notice this line:

<xsl:output method="html" />

This clues in the XSLT processor that the output is HTML, not the default XML format.

The XSLT program looks like a mixture of HTML and XML, and that's just what it is. Any XML elements that begin <xsl: are XSLT language statements. Anything else is text that is output verbatim.

For example, look at this line:

<b><xsl:value-of select="location" /></b><br/>

The xsl:value-of element directs the XSLT processor to find the location element in the XML input file, extract its value, and output that value in place of the xsl:value-of tag. The resulting output looks like this:

<b>Norfolk, Naval Air Station, VA</b><br/>

XSLT processors

As with other programming languages, like Perl or Ruby, you execute XSLT by running it through a language interpreter. This is often called an XSLT processor. But XSLT is not a general-purpose programming language—it exists to translate a single XML data file. So most XSLT processors require two input files: the XSLT program and the XML file it transforms.

Many Linux® distributions include a command-line XSLT processor called xsltproc. This is valuable for debugging and fine-tuning XSLT scripts, as are other similar tools.

Xsltproc expects two parameters: the XSLT program and the XML that XSLT program acts upon. Suppose I previously downloaded the NWS XML data for Norfolk Naval Air Station in Virginia to the file KNGU.xml (KNGU is the four-character unique Station ID described in Part 1). I can use this command to test my XSLT program from Listing 2:

xsltproc weather2html.xsl KNGU.xml

The XSLT processor applies the translations described in weather2html.xsl to input file KNGU.xml and writes the results to standard output. That output is shown in Listing 3.


Listing 3. Output from XSLT processing of KNGU.xml
                
<center>
<b>Norfolk, Naval Air Station, VA</b><br>
Fair<br>
<img border="0" 
     src="http://weather.gov/weather/images/fcicons/skc.jpg"><br>
57 F (14 C)<br>
Wind: From the West at 7 MPH<br>
Humidity: 58%<br>
Visibility: 7.00 miles<br>
<br>
<span style="font-size: 0.8em; font-weight: bold;">
  Last Updated on Oct 12, 7:53 am EDT
</span>
<br>
</center>

Approach 2: XSLT on the server

I want to apply the XSLT approach to the weather badge problem. In Approach 2, a server-side script pulls the data from the NWS server, uses XSLT to transform that XML to HTML, then sends the HTML snippet back to the browser. The browser then just inserts the snippet into a DIV tag.

Figure 1 shows the data pipeline used in this approach. Data flows from the NWS server into my server, where the server-side script converts XML to HTML. The pipeline ends in the browser, where the HTML is received and inserted into the Web page.


Figure 1. The data pipeline for Approach 2
Data pipeline for Approach 2

I need a server-side program that can perform the XSLT translation. I write this in Perl, as shown in Listing 4, but it can as easily be implemented in another language. The script is invoked by an XMLHttpRequest call on the browser. The Ajax JavaScript code sends the server-side script a single parameter: the four-character NWS Station ID needed to retrieve the XML data file from the NWS server.


Listing 4. The weather_xml2html.cgi Perl script
                
#!/usr/bin/perl

use strict;

my $DATA_DIR   = "/var/www/html/xml_weather/data";
my $XSL_FILE   = "$DATA_DIR/weather2html.xsl";
my $XSLTPROC   = "/usr/bin/xsltproc";
my $HTTP_BIN   = "/usr/bin/wget -q -O - ";
my $URL_FORMAT = "http://www.nws.noaa.gov/data/current_obs/%s.xml";

# Get the NOAA location key:
my $location = $ENV{QUERY_STRING};

# Minimal sanity check of the location key:
if ($location !~ /^[\d\w]{4}$/) {
    print "Content-type: text/html\n\n";
    print "Unknown location ($location).\n";
    exit 1;
}

# Build URL:
my $url = sprintf ($URL_FORMAT, $location);

# Build Command:
my $cmd = "$HTTP_BIN '$url' 2> /dev/null | $XSLTPROC $XSL_FILE - ";

print "Content-type: text/html\n\n";

open (my $IPIPE, "$cmd |");
while (<$IPIPE>) {
    print $_;
}
close $IPIPE;

My Apache Web server runs the script in Listing 4. The Apache QUERY_STRING environment variable provides the script with the four-character Station ID.

The Perl script uses two external commands: xsltproc and wget. Xsltproc is the command-line XSLT processor I described earlier. Wget is a free utility (available from the Free Software Foundation). It's preinstalled on most Linux distributions. Wget can grab a Web page (or other Web resource) off the Web. My Perl script uses wget to get the XML file from the NWS server.

The Perl script constructs a command pipeline that, if executed on the Linux command line, looks like this:

/usr/bin/wget -q -O - http://www.nws.noaa.gov/data/current_obs/KNGU.xml \
| /usr/bin/xsltproc /var/www/html/xml_weather/data/weather2html.xsl -

Using external programs

Some programmers might look aghast at the use of external programs to implement this little Ajax Web service. The Comprehensive Perl Archive Network (CPAN) repository contains thousands of Perl modules. Several of these can be used in place of either wget or xsltproc. But by using these two utilities, the weather_xml2html.cgi script is easier to port to other languages, such as Python or Ruby. Most scripting languages can pipe the output from external programs into a script. In practice, I recommend using the libraries or modules available in your language of choice.

The output from this command is read into the Perl script and sent to standard output. Remember that this script was invoked using XMLHttpRequest by the browser JavaScript code, so this output is sent back to the browser as a response.

Unlike the method used in Approach 1, no Apache proxy rule is required. The weather_xml2html.cgi script serves as an intelligent proxy, and because this script resides on my server, I avoid the same domain problem described in Part 1 (see Resources.

The server returns the formatted HTML for the weather badge, so the client-side weather badge library has little work to do. Listing 5 shows the JavaScript code required for this second approach.


Listing 5. The weather_badge_intel_proxy.js implementation of weather_badge()
                
function weather_badge (nws_id, div_name) {
    var ajax = new Ajax 
        ("/cgi-bin/xml_weather/weather_xml2html.cgi?",
         nws_id,
         "GET",
         function (req) {
            var div = document.getElementById (div_name);
            div.innerHTML = req.responseText;
         }
        );
    ajax.request ();
}

All that's required of this version of weather_badge() is to call the server script (providing it with the proper NWS station ID), then insert the returned HTML into the target DIV tag using its innerHTML property.

Pros and cons

This approach places the greater share of processing on the Web server. If only a few users are hitting the server, or if the server has lots of memory and cycles to spare, this approach works well.

Likewise, if you know your users are running on underpowered old desktop machines, this approach makes sense. The browser has little to do: just make the request and wait for the server to handle the heavy lifting.

Approach 3: Client-side XSLT

The third weather badge implementation again uses XSLT. This time, XSLT processing occurs in the browser. The same weather2html.xsl XSLT program (Listing 2) is used here. Figure 2 shows the data pipeline used in this approach.


Figure 2. The data pipeline for Approach 3
Data pipeline for Approach 2

The major browsers (Microsoft® Windows® Internet Explorer®, Firefox, and Opera) all support XSLT processing in some form. Firefox and Opera implement the XSLTProcessor object. Internet Explorer handles XSLT processing by extending the Document model.

Listing 6 shows the implementation of the Approach 3 weather_badge(). As in Approach 1, an Apache Web proxy rule must be in place so the browser can access the NWS server. (Remember that Ajax applications can only access data from the same server that delivered the original Web page. See Part 1 for more on this same domain problem.)


Listing 6. The weather_badge_cs_xslt.js implementation of weather_badge()
                
function weather_badge (nws_id, div_name) {

    // Get the XML file from the server.

    var ajax = new Ajax ("/nws_currobs/" + nws_id + ".xml", "", "GET", null);
    ajax.setAsync (false);
    ajax.request ();
    var xml_doc = ajax.req.responseXML;

    // Get the XSLT from the server.

    ajax = new Ajax ("/xml_weather/data/weather2html.xsl", "", "GET", null);
    ajax.setAsync (false);
    ajax.request ();
    var xsl_doc = ajax.req.responseXML;

    var div = document.getElementById (div_name);

    // Use object detection to find out if we have
    // Firefox/Mozilla/Opera or IE XSLT support.

    if (typeof XSLTProcessor != "undefined") {
        var xsl_proc = new XSLTProcessor ();
        xsl_proc.importStylesheet (xsl_doc);
        var node = xsl_proc.transformToFragment (xml_doc, document);

        div.innerHTML = "";
        div.appendChild (node);
    }
    else if (typeof xml_doc.transformNode != "undefined") {
        div.innerHTML = xml_doc.transformNode (xsl_doc);
    }
    else {
        div.innerHTML = "XSLT not supported in browser.";
    }
}

In Approach 1, I needed to retrieve the XML file from the NWS server. In this new approach, I also need to retrieve the XSLT file from my own server. The first half of Listing 6 shows the code needed to bring these new files into the weather_badge() function. These are returned through XMLHttpRequest as the JavaScript Document objects xml_doc and xsl_doc.

The second half of this weather_badge() function applies the XSLT transformation. Because Internet Explorer handles this differently from the other browsers, I need to figure out which type of browser I'm running on. I do this with object detection. I use the JavaScript typeof operator to check for the existence of the objects needed to perform the transformation.

If XSLTProcessor is defined, I have a Firefox or Opera browser. In this case, I instantiate a new XSLTProcessor object and import the XSLT style sheet into that object. Then I transform the XML weather data using the object's transformToFragment method. This method returns a document fragment, not the HTML text. This means I can't insert the results into the DIV tag using innerHTML, as I did in the first two approaches. The solution is nearly as easy: using appendChild, I can insert the resulting document fragment into the DOM tree of the page.

If XSLTProcessor is not defined, but the transformNode method is, then I assume the JavaScript program is running in Internet Explorer. In this case, a single line of code is all that I need to perform the XSLT translation and insert the results into the DIV tag:

div.innerHTML = xml_doc.transformNode (xsl_doc);

Pros and cons

This third approach uses some elements of the first two approaches. An Apache Web proxy brings the XML back to the browser for further processing, and XSLT handles the actual translation from XML to HTML.

To invoke an XSLT processor inside a browser is more computer-intensive than Approach 1, which merely accessed the needed data elements directly from the DOM tree. For this simple example, the extra compute time is unlikely to be noticed by the user. But if the XML is large or the XSLT translation is complex, then the user might notice an unacceptable delay while the browser churns out the results.

The flip side of this is that the code needed to manually trudge through the complex DOM tree of a large XML file can lead to JavaScript code that's difficult to write and more difficult to maintain.

You also need to consider the type of browser and computer your users are running. Are these high-end workstations with memory and processor power to spare? Or are they old, underpowered machines? The answers to these questions determine if complex JavaScript XSLT processing is a good idea for your application.

Conclusion

In the final installment of this series, I'll look at the final approach to this problem—a solution that's radically different from anything I've presented so far. It's so different, you might argue it's not even Ajax. This last solution sidesteps the same domain problem that's vexed these first three solutions, but it raises a whole new set of issues. Then I'll compare the four approaches and touch on a few of the many approaches I didn't present.



Download

DescriptionNameSizeDownload method
Sample code for this seriesx-xmlajax.zip194KB HTTP

Information about download methods


Resources

Learn

  • XML processing in Ajax, Part 1: Four approaches (Mark Pruett, developerWorks, March 2007): You can solve any programming problem in multiple right ways. Start with the Document Object Model (DOM) tree in this series that looks at four different approaches to create an Ajax weather widget.

  • XML processing in Ajax, Part 3: JSON and avoiding proxies (Mark Pruett, developerWorks, March 2007): In this last installment, use a public Web service, JavaScript Object Notation (JSON), and dynamic script tags for the final approach to the weather badge project.

  • What kind of language is XSLT? (Michael Kay, developerWorks, February 2001, updated April 2005): In this analysis and overview, find a good introduction to XSLT, where the language comes from, what it's good at, and why you should use it.

  • Mastering Ajax, Part 1: Introduction to Ajax (Brett McLaughlin, developerWorks, December 2005): Explore this multi-part series that looks at Ajax, a productive approach to building Web sites, and how it works.

  • Ajax resource center on IBM developerWorks: Check out this resource on how to create more interactive Web apps with Ajax technologies.

  • IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.

  • XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.

  • developerWorks technical events and webcasts: Stay current with technology in these sessions.

  • The technology bookstore: Browse for books on these and other technical topics.

Get products and technologies

  • IBM trial software: Build your next development project with trial software available for download directly from developerWorks.

Discuss

About the author

Mark Pruett is a Systems Architect at Dominion. He's a contributing author to O'Reilly Media's book Ajax Hacks and author of the O'Reilly Shortcuts Ajax and Web Services and Yahoo! Pipes.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Web development
ArticleID=293158
ArticleTitle=XML processing in Ajax, Part 2: Two Ajax and XSLT approaches
publish-date=03112008
author1-email=mark.l.pruett@dom.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers