This Site, XML-ified: Introduction
When I first became interested in writing my own websites back in 2002 or 2003, the only book I could find in my father's enormous bookshelf of computer books was Building Web Sites with XML. I could hardly understand the book at the time; HTML and CSS seemed like foreign languages from a distant planet. But like Slackware Linux and other computer-related interests I encountered after my discovery of XML-based websites, I eventually found myself back where I started. This time, in 2008, I had a fair understanding of object-oriented programming and, of course, HTML and CSS.
I already had this website marked up in HTML when I decided I would convert it to XML/XSLT, so most of the conversion was just figuring out how to convert my HTML into a template that could be applied to XML documents with XSLT. The rest was just for fun, especially with the existence of CDATA structures.
Oh, and just a warning: If you don't know HTML or XML that well, this page can seem pretty technical, as the topic seemed to me when I first began writing my own websites. What I'm trying to do with this page is just explain my site's XSLT for people who are curious how my site works and aren't familiar with XSLT and XPATH.
Here are the files referenced on this page for your reference: The full XSLT template, a sample XML document utilizing the XSLT template, the nav.xml file, the transforming PHP script, and the relevant portion of the .htaccess file.
By the way, I don't think many people ever used XML/XSLT back when it was more popular a decade ago or will use XML/XSLT any time in the future. The W3C seems to have put development on the XML specification on the back burner, especially with the upcoming candidate recommendation for HTML5 and its XHTML counterpart in 2012. Nonetheless, XSL and XPATH are still interesting languages, and I think they are worth studying.^ Back to Top
Making the Server Transform XML with XSLT
Before I could actually experiment with XSLT, I had to have a reliable way to transform XML documents using it. Simply adding an <?xml-stylesheet?> tag and serving the unprocessed XML document to the browser would work for most modern web browsers, but it didn't feel reliable enough for me because of the possible rendering differences between browsers. Having to test the XSLT template in multiple browsers like I do with Cascading Style Sheets (CSS) seemed like far more work than it was worth, so I decided I would have the transformation done on the server.^ Back to Top
Basic XSLT Document Structure
I began with a barebones XSLT template that would output XHTML. Here is the basic XML document structure I used for my XSLT template:
Here is the line-by-line breakdown:
Line 000: The first line is the XML declaration. It is required, I think, in all standard XML documents.
Line 001: This line says the XML document is an XSLT (XSL Transformations) version 1.0 stylesheet.
Line 002: This line defines the "xsl:" prefix. Basically, it says that the "xsl:" prefix in front of the tags within the document indicate that they are part of the XSLT namespace, as defined in http://www.w3.org/1999/XSL/Transform. The XSLT namespace doesn't necessarily have to have the "xsl:" prefix. I could just as easily define it "omg:" or even give it no prefix.
Line 003: This line says that any tags without a prefix are part of the XHTML namespace, as defined in http://www.w3.org/1999/xhtml. I could also have given XHTML tags a prefix, such as "html:," and let all tags without a prefix be XSLT tags, or I could've even given both namespaces prefixes.
Line 004: This was probably the most difficult part of this section: figuring out how to get the XSLT processor to make proper XHTML output that Internet Explorer and standards-compliant browsers would render in standards mode.
- omit-xml-declaration="yes" means the XHTML output won't have this line:
<?xml version="1.0" encoding="UTF-8"?>
Interestingly, having the XML declaration at the top of an XHTML document makes Internet Explorer render the webpage in quirks mode, even if you have the DOCTYPE declarations, so the CSS doesn't work as well as you'd expect.
- doctype-system and doctype-public are the DOCTYPE declarations found at the top of all standards-compliant XHTML documents. If you've ever peeked at the HTML source of standards-compliant webpages, you'd see something like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
The two lines above are the DOCTYPE declarations that the doctype-system and doctype-public attributes tell the XSLT processor to put in the output XHTML document.
Lines xxx-xxx: Read the comment. The last line is, of course, the closing tag for the XSLT stylesheet.^ Back to Top
Understanding the "Catch-All" Template
The "catch-all" template, as I call it, was the hardest part of XSLT for me to wrap my head around. But once I finally got a reasonable understanding of it, I felt I had a pretty good understanding of the <xsl:apply-templates> tag. Here it is:
Only one to five reasonable lines of code, depending on how it's written. One to five lines of code I copied from elsewhere that bothered me quite a bit, but the understanding of which helped propel me forward in writing XSLT templates.
Here's the line-by-line breakdown:
Line 002: This line states that this is a template applied to everything that matches @* and node(). @* and node() are both XPath wildcards; @* means any attribute node (e.g. name="nav") and node() means any node of any type, including element nodes, text nodes, attribute nodes, processing instruction nodes, namespace nodes, and comment nodes (1).
Line 003: The <xsl:copy> tag tells the XSLT processor to make a copy of the current node. That is, the node matched by the template. In this case, the node the template matches is any one of the nodes in the XML document that doesn't have another template matching it. Other template matches take precedence over this one (2).
Line 006: <apply-templates> tells the XSLT processor to apply the transformation on whatever is selected by the XPath statement in the "select" attribute. My comment from back then explains fairly well what the <apply-templates> tag does here: It tells the XSLT processor to find templates that match @* and node() that are children of the current node and apply them. Because this "catch-all" template matches @* and node(), it will be applied over and over again, copying the nodes to the output, until there is nothing left to match (i.e., no more child nodes). Hence, recursion.
Apparently, this "catch-all" template is actually called an identity template. Also, it looks like I may have been right about the template's being recursive (3).^ Back to Top
Templating the Main HTML Structure
For this, I took my basic HTML template and divided it into a bunch of sections that I wanted to be templated in the XSLT. But first, I had to determine what to name the root element node and the diffferent sections. I ended up naming the root node, unimaginatively, <page>. The basic XML document for this website looks like this:
I won't be reproducing the entire HTML structure here, as you can find it here, in the full XSLT template, but I'll describe a small portion to give you a general idea of how it works:
Line 1 tests if there is a <right> element under <page> and line 2 applies the template that matches right (<xsl:template match="right">) if it exists. The test isn't actually necessary and is even redundant, because if <right> doesn't exist, no templates will match it.
Line 4's <xsl:value-of> takes the string value of the contents of the <edited> tag and outputs it where the <xsl:value-of> is. The output from the basic XML document sample above would look like this: <span class="small"><i>Last Updated: January 1, 1900</i></span>^ Back to Top
The Main Navigation Bar
The main navigation bar was probably the hardest part of converting the HTML into an XSLT template, so I've saved it for last.
I wanted the XSLT to be able to detect the page that was shown and highlight the respective link, to prepend the site's URL to the link so that I only had to change one thing to fix the links when I uploaded the website, and to know not to prepend the site's URL to external links. It involved my first extensive use of conditional statements, named template calls, and variables. Here is the nav.xml file used in the templates for reference: nav.xml.
I called the navigation bar template from within the main HTML template body:
Line 000: Basically, the statement above says, "call the template named nav." Fairly self-explanatory so far.
After calling the template named "nav," the XSLT processor finds this:
Line 001: This line "declares" the template named nav. Notice that it doesn't have a match attribute because I don't want it to be applied to any elements in the XML document.
Lines 002-003: This line essentially serves the purpose of adding a "reference" to the document root of the file nav.xml and storing it in an XSLT variable. How exactly this reference is used will hopefully become clearer in the next few lines.
Lines 004-006: A for-each loop! This works just as it sounds. It loops through all the element nodes under the <navigation> node in the nav.xml XML document. The <navigation> node is the root element node in the nav.xml file. As the comments say, all the declarations and templates called from within the for-each loop can only access nodes in the nav.xml file.
Line 007: <xsl:choose> tags are used to wrap around the when/when/when/otherwise declarations that are basically analagous to the if/else if/else if/else statements in other programming languages.
Lines 008-015: Here, there's a <when> declaration that tests if the navigation link is an external link. The <call-template> that follows is a call to a template named "navprint." The <with-param> declarations are analogous to the function parameters found in other programming languages. Notice that the prefix parameter doesn't have a value because this <when> "executes" when the link is an external link.
The @ (at sign) in front of id, url, and title indicates that they are attributes of the link node. The <with-param>'s select attribute sets the value of the parameter equal to the values indicated by the XPath expressions within them. In this case, the parameters are equal to the link's id, url, and title attributes, respectively.
Lines 016-023: The rest does basically the same as lines 8-15, except without any test, because it can be assumed that if a link element's ext attribute either doesn't exist or isn't true, the link is an internal link.
Lines 024-026: Pretty self-explanatory, if you have a basic knowledge of HTML. They close the <choose> block, the <for-each> loop, and the <template>.
Lines 027-031: This is the "navprint" template that the "nav" template above calls. It has a bunch of <param>'s so that the XSLT processor doesn't freak out in the event that the template is "executed" without having any parameters passed to it.
Lines 032-040: Another <choose> and <when> declaration (see line 7 for more info). This is the actual code that writes out the <a>'s and <li>'s for the navigation bar for the case where the id of the page and the id of the link match. If they match, the link should be highlighted, as indicated by the id = "active" attribute in the <a> element.
Thus, the "nav" template decides whether or not a link is an external link, and the "navprint" template decides whether or not a link should be highlighted. When both decisions have been made, the "navprint" template outputs the actual HTML markup.
Lines 041-047: This is basically the same thing as lines 32-40, except the id attribute in the <a> element doesn't exist because links aren't to be highlighted if the page id and link id do not match.^ Back to Top
- Armstrong, Eric, et al. "How XPath Works." The J2EE 1.4 Tutorial: For Sun Java System Application Server Platform Edition 8.2. Sun Microsystems, 5 December 2005. Web. 27 March 2010. http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JAXPXSLT3.html.
- "XSLT: Identity templates." Ektron Knowledge Base. Ektron Inc., n.d. Web. 28 March 2010. http://dev.ektron.com/kb_article.aspx?id=492.
- Dmccreary [Dan McCreary], et al., eds. "Identity transform." Wikipedia: The Free Encyclopedia. Wikimedia Foundation, Inc., 17 August 2009. Web. 28 March 2010. http://en.wikipedia.org/wiki/Identity_transform#Example_using_XSLT