My First ColdFusion XML / XSLT Example

Posted September 6, 2007 at 2:05 PM

Tags: ColdFusion

Over the past few months, I have been learning a lot about XML and especially the use of XPath in ColdFusion. As a natural extension of that, I felt it was time to look into some ColdFusion XML Transformation stuff in the form of XSLT. I don't know anything about it other than I looked at it a long time ago and thought the syntax was just silly. Part of that may have influenced by the fact that I didn't understand XPath, so I thought it was worth re-examining.

As with any new learning endeavour, I must do some sort of Hello World example to get my feet wet. In this ColdFusion XSLT demo, I am taking a small XML document containing messages and outputting them in an HTML page:

 Launch code in new window » Download code as text file »

  • <!--- Define the ColdFusion XML document object. --->
  • <cfxml variable="xmlData">
  •  
  • <messages>
  • <message id="1">
  • <text>Hello World</text>
  • </message>
  • <message id="2">
  • <text>Eating kittens is just plain wrong!</text>
  • </message>
  • <message id="3">
  • <text>Honk if you love justice!</text>
  • </message>
  • </messages>
  •  
  • </cfxml>
  •  
  •  
  • <!--- Define the XSL Transformation (XSLT). --->
  • <cfsavecontent variable="strXSLT">
  •  
  • <!--- 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">
  •  
  • <!---
  • Bind this template to the root of the XML document
  • using the "/" match attribute.
  • --->
  • <xsl:template match="/">
  •  
  • <html>
  • <head>
  • <title>My ColdFusion XSLT Hello World</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Messages
  • </h1>
  •  
  • <!--- Loop over each message node. --->
  • <xsl:for-each select="//message">
  •  
  • <p>
  • <!---
  • Output the ID of the current
  • contextual message node.
  • --->
  • <xsl:value-of
  • select="@id"
  • />:
  •  
  • <!---
  • Get the Text value of the descendent
  • TEXT node of the current contextual
  • message node.
  • --->
  • <xsl:value-of
  • select="text"
  • />
  • </p>
  •  
  • </xsl:for-each>
  •  
  • </body>
  • </html>
  •  
  • </xsl:template>
  •  
  • </xsl:transform>
  •  
  • </cfsavecontent>
  •  
  •  
  • <!--- Output the transformed XML document. --->
  • #XmlTransform(
  • xmlData,
  • Trim( strXSLT )
  • )#

A few things to notice in this example. For starters, the XML that I am passing into ColdFusion's XmlTransform() function is a full-blown ColdFusion XML document object model. According to the documentation, I could have passed in an XML string as well.

The XSLT value that I am passing in is just an XML string, but according to the documentation this could have been:

  • A string containing XSL text.
  • The name of an XSTLT file. Relative paths start at the directory containing the current CFML page.
  • The URL of an XSLT file; valid protocol identifiers include http, https, ftp, and file. Relative paths start at the directory containing the current CFML page.

You will notice that I am trimming the XSLT value as I pass it into the function. Remember, XML documents (what the XSLT document really is) cannot have white space at the beginning of them or you get that "target matching" error. However, when dealing with XmlTransform(), the error is a bit more convoluted:

A [Transformer] object cannot be created that satisfies the configuration requested. This could be due to a failure in compiling the [XSL] text. javax.xml.transform.TransformerConfigurationException: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: The processing instruction target matching "[xX][mM][lL]" is not allowed.

A bit more wordy, but same error. Trimming the XSLT XML data as you pass it in makes sure that it compiles down properly in the black box.

My XSLT Transform document contains three main parts: the base template, the node loop, and the node values. I am still learning the syntax, which feels very foreign to me, but basically, the template that matches "/" binds the template to the root node of the XML document. Then, my for-each command loops over the nodes returned by the XPath, //message, which of course selected all message nodes anywhere in the document. Within each of the loop iterations, the "context" of the loop is the current Message node; therefore, commands within the loop must be relative to the current context node. That's why my value-of commands don't need a whole lot of explanation - one gets the text value of the Text node, the other gets the text value of the attribute, ID.

Anyway, running the code above, we get the following output:

Messages

1: Hello World

2: Eating kittens is just plain wrong!

3: Honk if you love justice!

I can already see that having a good understanding of XPath is really helping me understand the XSL Transformation functionality. Not sure if I see too much of a place for this in my programming just yet, but then again, knowing this functionality will open its own doors.

Oh, and also, those last two quotes are from the TV show, The Tick :) That was a sweet show!

Download Code Snippet ZIP File

Comments (9)  |  Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




I'm Too Young For This!

Reader Comments

Hi Ben,

Have you seen this bug in your travels with xml?

http://davequested.blogspot.com/2007_02_01_archive.html#117079695943973557

Posted by David on Sep 6, 2007 at 3:45 PM


@Dave,

I have posted a comment on your blog. I think I need to know more about how you are using the XML document as multiple reads should never corrupt anything??

Posted by Ben Nadel on Sep 6, 2007 at 3:54 PM


Hi Ben,

Just to be confusing, that "Dave" isn't me ;-)
...he is another "Dave" I know...

Posted by David on Sep 6, 2007 at 4:40 PM


Ok, now you confused me :) What are you talking about? You mean, that isn't your blog?

Posted by Ben Nadel on Sep 6, 2007 at 4:43 PM


The blog I linked to is the "other" David.
He is based in Cork, Ireland (last I heard), and he found the issue with XMLParse().

I guess when I read your blog, I thought of that "gotcha" and wondered if you had encountered it at all.

The main plan is us "David"s are going to take over the world, but don't tell anyone I told you!

Posted by David on Sep 6, 2007 at 4:55 PM


Ha ha. Good luck. When the revolution is over, don't forget about us little people :)

Posted by Ben Nadel on Sep 6, 2007 at 5:15 PM


XSLT is freakin awesome. It can be used for a lot of stuff, for example I use it as my email templates, which makes it really easy to update any email that gets sent out. A while back this saved me quite a bit of time when DOD changed to INFOCON 4 and had to change to all plain text emails. I also use XSLT for generating base DAO, gateway, and service objects. Definitely good stuff!

Posted by Dustin on Sep 7, 2007 at 9:22 AM


XSLT is convoluted as hell, but immensely useful in a pinch. And powerful as hell.

I wrote a mini-feed reader a while back that I wanted to support RDF, RSS2 and Atom 1.0 feed formats, but I didn't want to write 3 separate parsers. I used XSLT to convert all formats into RSS2 (only the elements that were directly transferable or a reasonable approximation) and then wrote and RSS2 parser. Magic. :-)

Posted by Rob Wilkerson on Sep 7, 2007 at 11:41 AM


@Dustin / Rob,

Both those things sound really cool. I agree that it is convoluted, but I think in the long run it will be good to know.

Posted by Ben Nadel on Sep 7, 2007 at 11:44 AM


Post Comment  |  Ask Ben


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting