Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: David Lund and Ryan Jeffords

Convert jQuery XML Documentation To HTML / PDF Using ColdFusion And XSLT

By Ben Nadel on
Tags: ColdFusion

A long time ago, I took the jQuery documentation XML off of www.visualjquery.com and converted it into a PDF using a lot of ColdFusion XmlSearch() calls and a ton of ArrayLen() logic. It ended up being a lot of code. Well, now that I have been doing a lot of learning about XSLT in ColdFusion, I thought it would be fun to redo the task using XSL transformations rather than ColdFusion logic.

It turns out, the ColdFusion and XSLT approach is sooo much easier. There's a lot less conditional logic and I think the code just looks a heck of a lot cleaner. I am still not sure where the balance is between using xsl:template vs. using xsl:for-each. At times, they seem to do the same thing so I'm not sure if there is a best practice as to which one to use.

Before we get into the code, you can check out the links below to see this in action:

Convert jQuery XML to XHTML

Convert jQuery XML to XHTML to PDF

I couldn't find the jQuery 1.2 XML documentation, so for this test, I just used the older jQuery 1.1 documentation. Hopefully the format of the XML has not changed and this algorithm should be applicable to the newest documentation as well.

  • <!---
  • Read in the jquery XML documentation. Unfortunately, I
  • had a real hard time trying to find the most up-to-date
  • XML docs, so I had to go with the quite outdated 1.1.
  • --->
  • <cffile
  • action="read"
  • file="#ExpandPath( './jquery-1.1.xml' )#"
  • variable="xmlDocumentation"
  • />
  •  
  •  
  • <!---
  • Create the XSLT document that will transform out jQuery
  • XML into an XHTML web page.
  • --->
  • <cfxml variable="xmlTransform">
  •  
  • <!--- Document type declaration. --->
  • <?xml version="1.0" encoding="ISO-8859-1"?>
  •  
  • <xsl:transform
  • version="1.0"
  • xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  •  
  • <xsl:output
  • omit-xml-declaration="yes"
  • method="xml"
  • version="1.0"
  • encoding="UTF-8"
  • doctype-public="-//W3C//DTD XHTML 1.1//EN"
  • doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
  • indent="yes"
  • />
  •  
  •  
  • <!--- Match the root-level node. --->
  • <xsl:template match="/">
  •  
  • <html>
  • <head>
  • <title>jQuery Documentation</title>
  • <cfinclude template="jquery_stylesheet.txt" />
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery 1.1 Documentation
  • </h1>
  •  
  • <!---
  • Apply templates to direct descendents of the
  • root node (should be the CAT nodes).
  • --->
  • <xsl:apply-templates />
  •  
  • </body>
  • </html>
  •  
  • </xsl:template>
  •  
  •  
  • <xsl:template match="cat">
  •  
  • <h2>
  • <!---
  • Check to see if this category is a
  • sub-category. If so, we have to add the
  • conditional class attribute.
  • --->
  • <xsl:if test="../../cat">
  • <xsl:attribute name="class">
  • <xsl:text>sub</xsl:text>
  • </xsl:attribute>
  • </xsl:if>
  •  
  • <xsl:value-of select="@value" />
  • </h2>
  •  
  • <!---
  • Apply templates to all direct descendents of the
  • current category node.
  • --->
  • <xsl:apply-templates />
  •  
  • </xsl:template>
  •  
  •  
  • <xsl:template match="method">
  •  
  • <h3>
  • <xsl:value-of select="@name" />
  •  
  • <!--- Output the named params. --->
  • <span class="params">
  • <xsl:text>( </xsl:text>
  • <xsl:for-each select="params">
  •  
  • <xsl:value-of select="@name" />
  • <xsl:if test="position() != last()">
  • <xsl:text>, </xsl:text>
  • </xsl:if>
  •  
  • </xsl:for-each>
  • <xsl:text> )</xsl:text>
  • </span>
  • </h3>
  •  
  • <div class="methodbody">
  •  
  • <xsl:apply-templates select="desc" />
  •  
  • <xsl:if test="params">
  •  
  • <h4>
  • <xsl:text>Parameterss</xsl:text>
  • </h4>
  •  
  • <xsl:apply-templates select="params" />
  •  
  • </xsl:if>
  •  
  • <xsl:if test="options">
  •  
  • <h4>
  • <xsl:text>Hash Options</xsl:text>
  • </h4>
  •  
  • <xsl:apply-templates select="options" />
  •  
  • </xsl:if>
  •  
  • <xsl:if test="@type">
  •  
  • <h4>
  • <xsl:text>Returns</xsl:text>
  • </h4>
  •  
  • <p>
  • <xsl:value-of select="@type" />
  • </p>
  •  
  • </xsl:if>
  •  
  • <xsl:apply-templates select="examples" />
  •  
  • <xsl:if test="see">
  •  
  • <h4>
  • <xsl:text>See Also</xsl:text>
  • </h4>
  •  
  • <p>
  • <xsl:for-each select="see">
  • <xsl:value-of select="." />
  • <br />
  • </xsl:for-each>
  • </p>
  •  
  • </xsl:if>
  •  
  • </div>
  •  
  • </xsl:template>
  •  
  •  
  • <!---
  • This template will match params or options
  • which are formatted in the same way.
  • --->
  • <xsl:template match="params|options">
  •  
  • <p>
  • <xsl:if test="@name != ''">
  • <strong>
  • <xsl:value-of select="@name" />
  • </strong>
  • <xsl:text>: </xsl:text>
  • </xsl:if>
  •  
  • <xsl:if test="@type != ''">
  • <em>
  • <xsl:value-of select="@type" />
  • </em>
  • <xsl:text>: </xsl:text>
  • </xsl:if>
  •  
  • <xsl:value-of select="." />
  • </p>
  •  
  • </xsl:template>
  •  
  •  
  • <xsl:template match="desc">
  •  
  • <p class="description">
  • <xsl:value-of select="." />
  • </p>
  •  
  • </xsl:template>
  •  
  •  
  • <xsl:template match="examples">
  •  
  • <h4>
  • <xsl:text>Example</xsl:text>
  • </h4>
  •  
  • <xsl:if test="desc">
  • <p>
  • <xsl:value-of select="desc" />
  • </p>
  • </xsl:if>
  •  
  • <xsl:if test="code">
  • <p>
  • <xsl:value-of select="code" />
  • </p>
  • </xsl:if>
  •  
  • <xsl:if test="before">
  • <h5>
  • <xsl:text>Before</xsl:text>
  • </h5>
  •  
  • <p>
  • <xsl:value-of select="before" />
  • </p>
  • </xsl:if>
  •  
  • <xsl:if test="result">
  • <h5>
  • <xsl:text>Result</xsl:text>
  • </h5>
  •  
  • <p>
  • <xsl:value-of select="result" />
  • </p>
  • </xsl:if>
  •  
  • </xsl:template>
  •  
  • </xsl:transform>
  •  
  • </cfxml>
  •  
  •  
  • <!--- Output the jQuery XML documention transformation. --->
  • <cfset WriteOutput(
  • XmlTransform(
  • xmlDocumentation,
  • xmlTransform
  • )
  • ) />

The above code takes the XML and transforms it using ColdFusion's XmlTransform() to convert it into an XHTML document. Notice that I am using the xsl:text element around some text literals (ex. h3, h4, h5). This is not required. I am doing this because by using the xsl:text element, the ColdFusion transformation engine reduces the amount of white space that it puts into the resultant XHTML. Minor note, but I thought I would point it out.

Now, we can take that one step further (as I did in my first attempt) and convert that into a PDF using a very simple CFDocument call:

  • <!--- Create a PDF of the parse jQuery documentation. --->
  • <cfdocument
  • format="PDF"
  • pagetype="letter"
  • orientation="portrait"
  • unit="in"
  • encryption="none"
  • fontembed="Yes"
  • backgroundvisible="No">
  •  
  • <!--- Include the parsed jQuery XML. --->
  • <cfinclude template="./index.cfm" />
  •  
  • </cfdocument>

This XSLT stuff is really growing on me.




Reader Comments

Keep it up! Another thorough, real-world example.

Though I haven't done a ton of XSLT, in the real-world work I've done with it, using XmlTransform with XSLT has always out-performed nasty structure and array looping, etc. Do you find the same to be true with the performance of ths XSLT example vs. the original?

@Aaron,

It was definitely easier to code, which was cool. As a matter of processing performance, both work super fast. Although it would be curious to run it a bunch of times. Let me see what kind of comparison I can come up with.

Hi Ben!

I know this is a very late response / follow-up question to this post, but is it possible to do the reverse? That is, take an HTML document that contains tabular data and convert / transform it into XML?