| <?xml version="1.0" encoding="UTF-8"?> |
| <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
| "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
| <section id="ResucingXSLT"> |
| <title>Rescuing XSLT From Niche Status</title> |
| <subtitle>A Gentle Introduction to XSLT through HTML Templates</subtitle> |
| <note> |
| <para> |
| <emphasis> |
| Editors Note: This originally appeared at |
| <ulink url="http://www.xfront.com/rescuing-xslt.html">XFront.com</ulink> |
| , and is republished here with permission. The article is written by David Jacobs. |
| </emphasis> |
| </para> |
| </note> |
| <abstract> |
| <para> |
| XSLT is one of the most exciting technologies to come out of the XML family. Unfortunately, its incredible |
| power and associated complexity can be overwhelming to new users preventing many from experimenting with it or |
| causing them to quickly give up in disgust. In fact, unless the method of teaching and the common style of use |
| for XSLT is radically changed to make it more accessible, XSLT will be relegated to niche status like SGML and |
| other powerful technologies. |
| </para> |
| </abstract> |
| <section> |
| <title>The Problem</title> |
| <para> |
| The 1990’s saw an incredible proliferation of new web related languages. Looking back we can see what |
| features separated the winners and losers. The biggest key has been having a very low barrier to entry. Many |
| languages accomplished this by following the following rules. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para> |
| Leverage as much existing knowledge as possible. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Make it easy to get started with "hello world" style example. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The most common and useful language constructs should be easily conveyed with a few small examples. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Complex programming constructs should only be required when what is trying to be accomplished is |
| complex. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| From these rules we can see why embedded web scripting languages like Active Server Pages (ASPs), Cold |
| Fusion, PHP and |
| Java Server Pages (JSPs) are so popular. They all leverage a user’s knowledge of HTML. They also |
| allow the |
| minimum amount of scripting to be added to accomplish the dynamic feature a developer is looking for. |
| This has |
| allowed numerous web developers to start off with very small projects and then through continuos |
| enhancement |
| and learning, find themselves using the full power of a complex programming language. Furthermore, |
| because of |
| the very incremental nature of that learning the developer was never scared off. |
| </para> |
| <para> |
| HTML has also fostered the technique of learning by example. When a web author sees another site with a |
| feature they |
| like, they immediately bring up the source to see how it was implemented. In this way many web |
| authors were |
| able to learn complex HTML tricks with no formal training. While server-side scripts are not as |
| easy to come |
| by, there are still numerous sites that house thousands of example scripts for a blossoming |
| developer to |
| examine. |
| </para> |
| <para> |
| Traditionally XSLT has been presented as a programming language for translating XML documents into another |
| format, often for presentation. This frames the problem, such that for each element, the programmer has the |
| task of figuring out how that element needs to be translated. As long as there are one to one mappings or one |
| to zero mappings this is straightforward. For example, if every occurrence of a <![CDATA[ |
| <name>]]> |
| element is going to |
| become an HTML header. It is a simple matter to write a matching template to accomplish |
| this. |
| </para> |
| <programlisting id="pl_NameTemplate"> |
| <![CDATA[ |
| <xsl:template match="name"> |
| <h1><xsl:apply-templates/></h1> |
| </xsl:template> |
| ]]> |
| </programlisting> |
| <para> |
| However when adding one to many mappings (i.e. when an element’s contents will appear multiple times in the |
| target document with different formatting), keeping track of all the relationships quickly grows in complexity |
| and becomes confusing. For example, if, after writing the previous template, the programmer discovers that the |
| name also needs to be placed in the title the programmer might add the template |
| </para> |
| <programlisting id="pl_valueOf"> |
| <![CDATA[ |
| <xsl:template match="/"> |
| <title><xsl:value-of select="name"/></title> |
| </xsl:template> |
| ]]> |
| </programlisting> |
| <para> |
| Notice the use of the <![CDATA[<xsl:value-of>]]> |
| function in this template because using <![CDATA[<apply-templates select="name">]]> |
| would have caused a triggering of the previous template adding undesired header tags to my content. This means |
| before adding a translation to an element the programmer must first be aware of all the existing translations |
| (ugh!). Of course if the programmer became aware of the |
| <![CDATA[<title>]]> |
| requirement first, the contents of these templates could have been reversed. One can quickly see how |
| the |
| arbitrary decisions of development and discovery of requirements can lead to a set of templates that are |
| no |
| longer intuitive. |
| </para> |
| <para> |
| As a programmer with more than 20 years experience with over a dozen languages, XSLT templates and default |
| rules were |
| not obvious to me. Over the past year or two I had looked at numerous examples trying to discern how |
| they |
| worked. While I could understand the general gist of what was occurring, there was too much implied |
| behavior |
| that I did not pick up. It was not until going through formal XSLT training that I fully understood how |
| XSLT |
| worked. Clearly, if the barrier to entry is that high for an experienced programmer, the average web |
| developer |
| was not going to find this technology very useful. |
| </para> |
| </section> |
| <section> |
| <title>The Solution</title> |
| <para> |
| So how do we solve this problem and help deliver XSLT’s promise to the masses? For XSLT to be successful it |
| must be |
| presented and used in a way that adopts those attributes discussed earlier (reuse of knowledge, fast |
| start, and |
| gradualism). This tutorial will attempt to ease XSLT’s introduction by focusing on these attributes. |
| First, it |
| is only going to focus on the generation of HTML documents and users who are familiar with HTML. If |
| your goal |
| is to immediately start transforming one XML document into another XML document this tutorial is not |
| for you. |
| </para> |
| <para> |
| The second is to reframe the problem so the XSLT solutions programmers write are more naturally extensible |
| and |
| intuitive. Instead of trying to translate an XML source document into an HTML presentation document, the |
| programmer should see the XML document as a complex data structure with XSLT providing powerful tools for |
| extracting that information into their HTML documents. This allows us to leverage the experience most people |
| have with using an HTML templating language (e.g. ASP, PHP, JSP, Cold Fusion, Web Macro, etc). These templating |
| languages are all based on the basic premise that HTML comes first and all enhancements are then embedded in |
| special tags. |
| </para> |
| <para> |
| With some caveats, this tutorial will show how XSLT can be used in this same way. The benefit of this |
| approach is it |
| allows the quick use of many of XSLT’s powerful functions while letting you learn its more |
| esoteric |
| capabilities as the need arises. In addition the resulting XSLT files are more intuitive and |
| maintainable. |
| </para> |
| <programlisting> |
| <emphasis role="bold"><![CDATA[<xsl:value-of> and {}]]></emphasis> |
| </programlisting> |
| <para> |
| On to an example. Here is a very simple welcome page. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| Welcome! |
| </body> |
| </html> |
| ]]> |
| </programlisting> |
| <para> |
| And here is an XML document with information on member. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| Welcome <xsl:value-of select="member/name"/>! |
| </body> |
| </html> |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <para> |
| There are a couple of things that need to be pointed out right away. First this is a well-formed XML |
| document. This mean all HTML used must conform to the XHTML specification (i.e. all tags must be closed and |
| lowercase). |
| </para> |
| <para> |
| The lines before the <![CDATA[<html>]]> |
| tag and after the <![CDATA[</html>]]> |
| tag will be seen in all the examples. For now, other than realizing that they are required in any style sheet |
| created, just go ahead and forget they are there. You don’t NEED to understand them right now to get useful |
| work out of XSLT. |
| </para> |
| <para> |
| Notice the HTML is identical to the original except for the introduction of a new tag |
| <![CDATA[<xsl:value-of>]]>. |
| This tag is the key to extracting any piece of information out an XML document. It has a "select" |
| attribute that |
| provides the path through the XML document to the information we seek. In this case |
| <![CDATA[<member>]]> |
| is the outer most tag and |
| <![CDATA[<name>]]> |
| is the tag underneath it. Slash characters ("/") are used to designate parent/child relationships |
| between tags. |
| If you are used to navigating around a Unix file system this should feel familiar. |
| </para> |
| <para> |
| Now let’s consider further customizing this page by making the welcome in the person’s favorite color using the |
| <![CDATA[<font>]]> |
| tag with the "bgcolor" attribute. Because |
| <![CDATA[<xsl:value-of>]]> |
| is an XML tag it is not valid to insert it in an HTML attribute value. So another mechanism |
| is needed to insert |
| information from our XML file there. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| <font bgcolor="{member/favoriteColor}"> |
| Welcome <xsl:value-of select="member/name"/>! |
| </font> |
| </body> |
| </html> |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <para> |
| Notice the use of the curly brackets ("{}"). When used within an attribute assignment "{path}" has the exact |
| same effect as |
| <![CDATA[<xsl:value-of select="path" />]]> |
| used outside of attribute assignments. |
| </para> |
| </section> |
| <section> |
| <title>Queries</title> |
| <para> |
| Not all paths lead to a single node. For example, what if we wanted to put a person’s home phone number on the |
| page? Notice that the XML document contains two phone entries. If we simply used |
| <![CDATA[<xsl:value-of select="member/phone" />]]> |
| both entries would be returned. We obviously need a way to be more specific. Luckily, XSLT allows the full |
| power of XPath to describe the value(s) of interest. XPath allows conditions on any attribute or tag to be |
| placed in square brackets ("[]") which are then used to restrict the values returned. |
| </para> |
| <para> |
| So to retrieve the home phone number we would use the path "member/phone[@type=’home’]". Notice the "@" |
| symbol in front |
| of "type". The "@" symbol signifies that we are referring to an attribute. So our new HTML |
| template looks like: |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| <font bgcolor="{member/favoriteColor}"> |
| Welcome <xsl:value-of select="member/name"/>! |
| <br/> |
| Your home phone number is: |
| ]]> <emphasis role="bold"><![CDATA[<xsl:value-of select="member/phone[@type=’home’]"/>]]></emphasis> |
| <![CDATA[ </font> |
| </body |
| </html |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <section> |
| <title>xsl:for-each</title> |
| <para> |
| The previous example brings up another issue. What if this |
| <![CDATA[<member>]]> |
| entry had numerous phone numbers and we wanted to print them all on the web page. We could simply use |
| <![CDATA[<xsl:value-of select="member/phone" />]]> |
| but this would not enable us to format the phone number into a nice list that describes the type of each |
| number. |
| </para> |
| <para> |
| To accomplish this requires the introduction of the |
| <![CDATA[<xsl:for-each>]]> |
| tag which allows us to loop through each of the elements that match a given path. So to |
| create a table that |
| contains the phone number type in the first column and the phone number in the second |
| column, the following |
| stylesheet could be used. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| <font bgcolor="{member/favoriteColor}"> |
| Welcome <xsl:value-of select="member/name"/>! |
| </font> |
| <table> |
| <tr><th>Type</th><th>Number</th></tr> |
| <xsl:for-each select="member/phone"> |
| <tr> |
| <td><xsl:value-of select="@type"/></td> |
| <td><xsl:value-of select="."/></td> |
| </tr> |
| </xsl:for-each> |
| </table> |
| </body> |
| </html> |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <para> |
| This example brings up a number of issues. First, while in the loop, all |
| <![CDATA[<xsl:value-of />]]> |
| accesses are relative to the current element being iterated over (in this case |
| <![CDATA[<phone>)]]>. |
| Notice the use of the period ("."), which like in a Unix file system means the current element. |
| So in this |
| case the period (".") refers to each phone element as the loop iterates. Also like in a file |
| system you can |
| address a parent element using a double period ("..") and can access any element in the |
| document by starting |
| over at the root element using a slash ("/"). |
| </para> |
| </section> |
| <section> |
| <title>xsl:if</title> |
| <para> |
| As a last enhancement to our page let’s add a special offer to "platinum" level members. To do this requires |
| the use of a new tag |
| <![CDATA[<xsl:if>]]> |
| which allows us to insert content based on a condition of the data in the XML document. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| <font bgcolor="{member/favoriteColor}"> |
| Welcome <xsl:value-of select="member/name"/>! |
| </font> |
| <xsl:if test="member[@level='platinum']"> |
| Our special offer to platinum members today is something great |
| </xsl:if> |
| <table> |
| <tr><th>Type</th><th>Number</th></tr> |
| <xsl:for-each select="member/phone"> |
| <tr> |
| <td><xsl:value-of select="@type"/></td> |
| <td><xsl:value-of select="."/></td> |
| </tr> |
| </xsl:for-each> |
| </table> |
| </body> |
| </html> |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <para> |
| Within the "test" attribute the full array of Boolean and relative operators are available. The only caveat |
| is that since this is an XML document less than and greater than (<![CDATA["<"]]>, |
| ">") signs must be escaped as <![CDATA["<" and ">"]]>. |
| </para> |
| </section> |
| <section> |
| <title>xsl:choose</title> |
| <para> |
| One nuance of the |
| <![CDATA[<xsl:if>]]> |
| tag that is not always obvious at first glance is the lack of an "else" statement. This means to have an |
| else statement requires two ifs. The first one saying "if condition" and the second one saying “if not |
| condition”. This scheme quickly becomes unworkable with embedded if then else logic. Luckily XSLT |
| supports an |
| additional test operator called |
| <![CDATA[<xsl:choose>]]> |
| which works like a switch/case statement. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
| version="1.0"> |
| <xsl:template match="/"> |
| <html> |
| <head> |
| <title>Welcome</title> |
| </head> |
| <body> |
| <font bgcolor="{member/favoriteColor}"> |
| Welcome <xsl:value-of select="member/name"/>! |
| </font> |
| <xsl:choose> |
| <xsl:when test="member[@level='platinum']"> |
| Our special offer to platinum members today is something great |
| </xsl:when> |
| <xsl:otherwise> |
| Become a platinum member today! |
| </xsl:otherwise> |
| </xsl:choose> |
| <table> |
| <tr><th>Type</th><th>Number</th></tr> |
| <xsl:for-each select="member/phone"> |
| <tr> |
| <td><xsl:value-of select="@type"/></td> |
| <td><xsl:value-of select="."/></td> |
| </tr> |
| </xsl:for-each> |
| </table> |
| </body> |
| </html> |
| </xsl:template> |
| </xsl:stylesheet> |
| ]]> |
| </programlisting> |
| <para> |
| The “test” attribute has the same capabilities/ constraints as the “test” attribute in the |
| <![CDATA[<xsl:if>]]> |
| tag. Multiple |
| <![CDATA[<xsl:when>]]> |
| blocks are allowed. As soon as one “when test” is mached, it will not evaluate any further “xsl:when |
| tests” |
| in the |
| <![CDATA[<xsl:choose>]]> |
| block. |
| </para> |
| </section> |
| </section> |
| <section> |
| <title>Conclusion</title> |
| <para> |
| With just these few commands (an admittedly small subset of XSLT) and a strong background in HTML (DHTML and |
| JavaScript |
| included), I believe web developers could meet the majority of their presentation needs. Obviously |
| there will |
| be cases where greater flexibility is required, but the advantage of this technique is that only |
| then, does the |
| developer need to learn those constructs. |
| </para> |
| <para> |
| As a further benefit, this technique minimized the interdependence of one XLST construct on another. Local |
| changes stay |
| local thereby reducing the brittleness of solutions. The developer also no longer has to remember |
| and account |
| for XSLT’s default behaviors. |
| </para> |
| <para> |
| I hope from these few examples, I have opened a few eyes to the power of XSLT and how a small change in how |
| XSLT is framed can make a huge difference in its understandability and accessibility to web developers. If |
| nothing else, I hope to encourage some good discussions. |
| </para> |
| </section> |
| </section> |