Convert jQuery XML Documentation To HTML / PDF Using ColdFusion And XSLT
Posted September 20, 2007 at 9:02 AM by Ben Nadel
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 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?



