Data in XML format will play a significant role for the foreseeable future.

Moreover, it's clear that XML and XSL will play a significant role in most, if not all, of my future applications. Why is it, however, that almost everything we read talks about merging XML+XSL on the client side, which requires IE 5 or higher browsers? In this article, Steve shows how to apply XSL transformations on the server to get around this problem.

I've seen the light, and I'm hooked. Data in the form of XML will play a significant role in information technology for the foreseeable future. Moreover, it's clear that XML and XSL will play a significant role in most, if not all, of my future applications.

A useful optimization is to use server-side merging only when needed.

XSL (eXtensible Stylesheet Language) is a form of XML (eXtensible Markup Language). XML is typically used to convey data, and XSL is used to convey style and formatting. Thus, XML is about data, and XSL is about presenting data. The key notion here is the physical separation of the data (XML) from its representation (XSL), so both can be reused separately.

But, have you noticed that almost all of the XML+XSL samples at Microsoft's MSDN website involve client-side merging of XML and XSL? In almost all of the sample materials, the client browser is responsible for downloading the XML and XSL, and merging them to produce content for display.

Which browser? Why, IE 5 or better from Microsoft, of course! Netscape and other browsers, including all the down-level browsers in today's phones, pagers, and palm devices, don't play in the client-side XML+XSL space. Client-side XML+XSL is for situations where you can specify Internet Explorer clients.

Here's the typical way a client-side XML+XSL merge happens in IE 5:

<HTML>
  <HEAD>
    <TITLE>XML+XSL client-side merge</TITLE>
  </HEAD>

  <XML id="source" src="conf.xml"></XML>
  <XML id="style" src="conf.xsl"></XML>

  <SCRIPT FOR="window" EVENT="onload">
    xslTarget.innerHTML = source.transformNode(style.XMLDocument);
  </SCRIPT>

  <BODY>
    <P STYLE="font-size:10pt; font-family:Verdana; color:gray">
      <B>This demo shows loading XML source and XSL style sheets and inserting the transformed result into the Web page.</B>
    </P>
    <DIV id="xslTarget"></DIV>
  </BODY>
</HTML>

In short, the OnLoad() event triggers script that calls the transformNode event of the Microsoft.XMLDOM object. In IE 5 you'll see a result. In Netscape and all the other browsers you'll see nothing. If, like me, you feel that the dependence on IE 5 isn't realistic, then how do you leverage XML and XSL and still serve a wide variety of client browsers? The answer is to merge the XML and XSL on the server, and send just the resulting HTML back to the client.

This is ridiculously easy to do, by using the Microsoft XMLDOM control much like the client-side example above. In pseudocode:

  • Create and load an XMLDOM object for the XML; let's call it oSource.
  • Create and load an XMLDOM object for the XSL; let's call it oStyle.
  • The return value from calling oSource.TransformNode(oStyle) is your merged HTML.

End of story. Here's the actual FoxPro code for this:

*-- Create two objects, one to hold the XML...
oSource= CREATE("Microsoft.XMLDOM")
oSource.Async= .F.
oSource.Load("conf.xml") && could be a URL

*-- ... and another to hold the XSL
oStyle= CREATE("Microsoft.XMLDOM")
oStyle.Async= .F.
oStyle.Load("conf.xsl")

*-- You could equally send this to a static
*-- HTML page
RESPONSE.Write(oSource.TransformNode(oStyle))

Notice that we set the Async property to logical false to ensure that both the XML data and the XSL style are in a predictable state (that is, loaded and ready to go) before we call the transformNode method.

For example, you have the following XML in CONF.XML:

<?xml version="1.0"?>
<conference>
  <speakers>
    <speaker>
      <name>Terry Bradshaw</name>
      <id>16</id>
    </speaker>
    <speaker>
      <name>Rodney Dangerfield</name>
      <id>30</id>
    </speaker>
    <speaker>
      <name>Ross Perrot</name>
      <id>32</id>
    </speaker>
    <speaker>
      <name>Luciano Pavarotti</name>
      <id>256</id>
    </speaker>
  </speakers>
</conference>

You also have the following XSL in CONF.XSL:

<?xml version="1.0"?>
<xsl:template xmlns:xsl="http://www.w3.org/TR/WD-xsl";>
  <table cellspacing="2">
    <tr bgcolor="#6699FF">
      <th>Id</th><th>Name</th>
    </tr>
    <xsl:for-each select="conference/speakers/speaker" order-by="+ name">
      <tr bgcolor="silver">
        <td>
          <xsl:value-of select="id"/>
        </td>
        <td>
          <xsl:value-of select="name"/>
        </td>
      </tr>
    </xsl:for-each>
  </table>
</xsl:template>

This is the HTML resulting from the merge of CONF.XML and CONF.XSL:

<table cellspacing="2">
  <tr bgcolor="#6699FF">
    <th>Id</th><th>Updated</th>
  </tr>
  <tr bgcolor="silver">
    <td>256</td>
    <td>Luciano Pavarotti</td>
  </tr>
  <tr bgcolor="silver">
    <td>30</td>
    <td>Rodney Dangerfield</td>
  </tr>
  <tr bgcolor="silver">
    <td>32</td>
    <td>Ross Perrot</td>
  </tr>
  <tr bgcolor="silver">
    <td>16</td>
    <td>Terry Bradshaw</td>
  </tr>
</table>

This looks like Figure 1 when viewed in Netscape Navigator 4.08, and should be presentable in any browser that supports tables.

Figure 1 - The result of merging CONF.XML and CONF.XSL on the server, as seen in Netscape Navigator 4.08.
Figure 1 - The result of merging CONF.XML and CONF.XSL on the server, as seen in Netscape Navigator 4.08.

Keep in mind that even if you choose to implement server-side XML+XSL merging, you can still control where the XML+XSL merge actually takes place. If you detect that the client is an XML-capable browser, you can offload the XML+XSL merge to the client! Thus, your web server does extra work only when it must.

Server-side XML+XSL merging is ridiculously easy to do, by using the XMLDOM.

What should you retain from this? Despite the ridiculous level of emphasis on client-side XML+XSL merging, most developers will need server-side merging. Fortunately, this is a very simple proposition: create two XMLDOM objects (one for the XML data, the other for the XSL style). Then, call the XML data object's transformNode() method, passing in the XSL style, and you are done. It's extra work for the server, but you're compatible with the world, not just those with bleeding-edge IE 5 (or higher) installations.