Jay Kimble -- The Dev Theologian

Sponsors

The Lounge

Syndication

News

  • CodeBetter.Com Home
    Current Threat level
    Terror Alert Level

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
XSLT for the Uninitiated Part 2: Beginning XSLT

[So what do you do when a Florida storm wakes you up at 3:30am?  You get up and start an XSLT post]

First of all, sorry this has taken so long to post.  My mom passed away between posts.  Generally, I put up technical stuff that I have learned doing my normal job.  I learned XSLT awhile ago, and so have never really put it down, so you forget a few things here and there… I needed a little bit of time to put this post together.  Especially since the tools have gotten better.  VS2005 now has pretty nice XSLT support built-in (which I will describe).  Prior to this I had to resort to using various tools that I found online to do this.

Introduction
This topic builds on what we learned in XSLT for the Uninitiated Part 1: Beginning Xpath.  So if you haven’t read that post I would strongly suggest that you do so. 

We’ll start with a fairly simple XML file and do a fairly common task (turn it into tabular data). Here’s our XSLT file:

<?xml version="1.0" encoding="utf-8" ?>
<data>
  <person>
    <name First="Jay" Last="Kimble" />
    <address line="1">123 Test</address>
    <address line="2">Box 778</address>
    <city>Some Place</city>
    <state>FL</state>
    <postal>12345</postal>
    <country>USA</country>
  </person>
  <person>
    <name First="Bill" Last="Gates" />
    <address line="1">1234 Test</address>
    <city>Some Place</city>
    <state>WA</state>
    <postal>54321</postal>
    <country>USA</country>
  </person>
  <person>
    <name First="Don" Last="Xml" />
    <address line="1">234 Test</address>
    <city>Some Place</city>
    <state>NJ</state>
    <postal>54321</postal>
    <country>USA</country>
  </person>
</data>


So we have data with person elements in it.  Let me give you a very basic tabular XSLT and then I’ll discuss it.

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>
  <xsl:template match="/">
    <table>
      <tr>
        <th>Name</th>
      </tr>
      <xsl:apply-templates select="//person" />
    </table>
  </xsl:template>

  <xsl:template match="person">
    <tr>
      <td>
        <xsl:value-of select="
./name/@First"/>&#160;<xsl:value-of select="./name/@Last"/>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Let’s take this line by line.
1) Skipping XML Declaration (if you need to know about it go look it up)
2) <xsl:stylesheet version="1.0"
    xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
This is the standard Stylesheet declaration.  We are using the 1.0 spec (and, yes, there is a 2.0 spec out there).  The “xmlns:xsl” defines where the definition for the “xsl:“ namespace comes from (BTW, namespaces in XML are defined with a colon preceding the element). 
3) <xsl:output method="html"/> A rather interesting XSL tag that lets the processor know that we will be outputting html instead of XML, so it will make modifications to the output tree if the root output node is html.  You can also output text and XML.  “text” means that the tags will be stripped and only the text part of the tags will be available.  The important thing to note is that you must produce valid XML (with matching open and close tags) in your output.  This element will cause the processor to do different things with the output once it has been generated.
4) <xsl:template match="/"> This is the first template and it is the main template.  All nodes that match the xpath expression ‘\’ (root node) are processed.  .
5) <table></tr>We’re writing standard HTML tables at this point.
6) <xsl:apply-templates select="//person" /> this essentially calls all templates that match on “person” nodes
7) </table>…</xsl:template> Our HTML must be valid XML so we complete the tags from above ending the table and the template
8) <xsl:template match="person"> Defines a new template that matches “person” nodes (hey we call one of those in the <asl:apply-templates> tag)
9) <tr><td> “Again with the HTML tags.”
10) <xsl:value-of select="&#160; ./name/@First"/> Finally, we are outputting some data from the XML file.  We are starting at the current node (a person node), finding the child name node and outputting this name node’s First attribute (if you’re confused go back to the first post on xpath… this is an xpath expression).
11) &#160;   This one is tricky.  We need a non-blocking space, but the &# combination is reserved for some other things in XML/XSLT, so use the method to output the ascii character that means non-blocking space.  We’ll deal with this later so you can have ‘&nbsp;’
12) <xsl:value-of select="./name/@Last"/>… </xsl:stylesheet> Finally, output the last name (in the same manner we did the first name) and close all tags including the stylesheet tag.

Sorting
That data looks awful unsorted.  Let’s make some changes to sort it.

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"
    xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>
  <xsl:template match="/">
    <table>
      <tr>
        <th>Name</th>
      </tr>
      <xsl:apply-templates select="//person" >
        <xsl:sort select="./name/@Last"/>
      </xsl:apply-templates>
    </table>
  </xsl:template>

  <xsl:template match="person">
    <tr>
      <td>
        <xsl:value-of select="
&#160;./name/@First"/>&#160;<xsl:value-of select="./name/@Last"/>
      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

We only added one element really: the <xsl:sort> element which is all that is needed to sort the data.  <xsl:sort> is used to sort the nodeset (results of an xpath select on an XML document or XML fragment)   It can be used inside of an <xsl:apply-templates> or <xsl:for-each> tag, and it does the same thing in each (sorts the nodes that are being processed before they are processed) 

Entities – Character Replacements
How to get rid of that &#160; and replace it with &nbsp;.  Add these lines right after the <?xml ?> tag:
<!DOCTYPE xsl:stylesheet [
  <!ENTITY nbsp "&#160;">
]>

They define an element which is a character replacement (BTW, I had to look this one up here… I had forgotten exactly how to do it).  All we really did was make our text more HTML like (the processor is still using the &#160; )

Show All the data
Here’s the complete XSLT stylesheet that I created:

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE xsl:stylesheet [
  <!ENTITY nbsp "
&#160;">
]>
<xsl:stylesheet version="1.0"
    xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>
  <xsl:template match="/">
    <table>
      <tr>
        <th>Name</th>
        <th>Address</th>
      </tr>
      <xsl:apply-templates select="//person">
        <xsl:sort select="./name/@Last"/>
      </xsl:apply-templates>
    </table>
  </xsl:template>

  <xsl:template match="person">
    <tr>
      <td style="vertical-align:top">
        <xsl:value-of select="&nbsp;./name/@First"/>&nbsp;<xsl:value-of select="./name/@Last"/>
      </td>
      <td style="vertical-align:top">
        <xsl:for-each select="./address">
          <xsl:sort select="./@line"/>
          <xsl:value-of select="."/><br />
        </xsl:for-each>
        <xsl:value-of select="./city"/>,
        &nbsp;<xsl:value-of select="./state"/>
        &nbsp;<xsl:value-of select="./postal"/>
        <br />
        <xsl:value-of select="./country"/>
        <br />
      </td>
    </tr>

  </xsl:template>
</xsl:stylesheet>

Honestly, you should know how it works except the <xsl:for-each > statement which I only used here for the sake of discussion (I could have used another <xsl:apply-templates> with a template that matched address nodes).  <xsl:for-each> loops through each node in the select list and let’s you output the data.  You can also use a xsl:sort with a xsl:for-each to sort the data before the loop occurs.

Some XML purists don’t like the <xsl:for-each> because it coddles certain procedural programming behaviors (XSLT is what is called declarative programming).  For what its worth, until this article, I usually only ever used <xsl:for-each>, but will probably be avoiding it going forward.

Closing thoughts
I ran out of time; I really wanted to show you how to show you how to use this with XSLT with AJAX.  I guess we’ll save that for the next post which will be on advanced techniques (hopefully it won’t take so long this time).  I plan on talking about rendering client-side as well server-side; and there are a few advantages to each; we’ll also talk about some techniques for dynamic sorting..  I’ll also be releasing an update of my JAAJAX library between now and then.

If you can’t wait and want to dig deeper, let me recommend a book to you since it has been invalueable to me (and I don’t even have an amazon account to make some cash off of you ).  The book will cost you about 12.95 USD.  It’s the XML: Pocket Reference (from O’Reilly) by Robert Eckstein with Michael Casabianca (I won't link it... find it yourself).  Believe it or not this is the only book I’ve read on the subject.  And for a pocket reference it is excellent (mine is falling apart).

Technorati Tags:


Posted 04-10-2006 4:07 PM by Jay Kimble

[Advertisement]