Disclaimer: I am very new to XSLT. This is the stuff that I have worked out in my head. Some it might be incomplete. Some of it might be dead wrong.
XSL stands for "Extensible Stylesheet Language". XSL Transformation (XSLT) provides us with a way to convert any XML document into another XML document. Apparently, there are parts of the XSL language that have to do with formatting, but for our purposes, we are going to concentrate on the transformation aspects only.
ColdFusion supports a subset of the XSLT functionality. While many features of the XSLT language are implemented in ColdFusion, there are a number of functions that are not supported. I am not sure exactly if the set of supported features is listed anywhere, but we should be able to do some really complex stuff without the entire feature set at our disposal.
Before you do anything in XSLT (ColdFusion or otherwise), you are going to need to have a firm grasp on XPath. XPath is a way to pull nodes out of an XML document and it is only through XPath that we can find the XML nodes we wish to include in our transformations.
There are more XSLT elements than the ones listed below, but with just these few elements, we can perform every high level transformations. NOTE: All XSLT elements (and all XML elements) should be all lower case when part of an XSLT document; I am simply camel casing them here for my own preference.
This is the root element of the XSLT document. It provides information about the transformation including the namespace of the XSLT nodes.
This defines rules to be applied to nodes within the XML document. You can think of this as being somewhat equivalent to a ColdFusion custom tag.
This applies a template to the selected nodes. You can think of this as being somewhat equivalent to a ColdFusion CFModule tag that executes a custom tag (see Template above).
This outputs the selected value of the selected or current node. You can think of this as being somewhat equivalent to a ColdFusion CFOutput tag.
This loops over the nodes in the selected node set. You can think of this as being somewhat equivalent to a ColdFusion CFLoop tag.
This tests to see if a condition is true. There is no ELSE element. If you need to check multiple conditions, see Choose (below). You can think of this as being somewhat equivalent to a ColdFusion CFIF tag.
These tags work in conjunction to test for multiple conditions. These take the place of an IF / ELSE statement and you can think of these as being somewhat equivalent to the ColdFusion CFSwitch / CFCase / and CFDefaultCase tags respectively.
This tag allows you output text literals. You can think of this as being somewhat equivalent to an XHTML PRE tag.
These tags allow you to build dynamic nodes in the resulting XML document.
ColdFusion provides the XmlTransform() function as a way to apply XSL Transformations to an XML document. While the XmlTransform() function takes three parameters, we will only cover the first two for this introduction:
XML: The first argument must be either a ColdFusion XML document object or properly formatted XML string.
XSLT: The second argument must be one of the following:
ColdFusion will take the two arguments and return the transformed XML document (in string format).
For the examples in this introduction, we are going to be working off of this ColdFusion XML document object:
Launch code in new window » Download code as text file »
Here, we have nested elements, element nodes, attribute nodes, and text nodes; this should give us plenty to test with. For the examples, we are not going to show the Transformation taking place or, in most cases, the Transform element, but it would be something like this:
Launch code in new window » Download code as text file »
Many of the XSLT elements use either a Select or Match attribute. These take XPath paths that return a set of nodes. What is done with those returned nodes depends on the element in question.
Each XSLT document must contain one and only one Transform element (can also be swapped with a Stylesheet element) and it must be the root element.
Launch code in new window » Download code as text file »
Here we are defining the root node and the namespace of all the XSLT elements as being "xsl". This is simply "boiler plate" and needs to be done for every XSLT document (it doesn't need to be exactly like this, but this is a good place to start).
In order for the transformation to result in any useful output, at least one template must be applied to the incoming XML document. The most basic attribute (that we will be looking at) of the Template element is the Match attribute. The Match attribute tells the transformation engine which nodes to which the template should be applied. The Transformation engine will iterate over the XML document in a depth-first approach and apply templates when possible. If the Transformation hits a text node and no template has been applied, it will simply echo the text node value in the resulting document.
Here, we can use the XPath "/" to match the root element of the XML document:
Launch code in new window » Download code as text file »
This will be executed only once as there is only one root element (outputting the text "Here" once). Note that none of the text node values are echoed because the Transformation engine must apply the template before it gets to the text nodes.
If we define a template that matches more than one node, it will be executed for every matching node in the selected node set. Here, we can use the match "name" to match "name" element nodes anywhere in the XML document:
Launch code in new window » Download code as text file »
This will be executed once for each of the "name" element nodes and will result in "Here" being output 5 times.
The values of the Match attribute can be complex, but do not support all XPath values. here are some additional Match attribute values that are supported:
Launch code in new window » Download code as text file »
Using the Template element, we can get ColdFusion to apply implicit transformations based on patterns (match attribute). However, most of the time we are going to want to explicitly execute templates from within other templates. Apply-Templates allows us to do just that.
The Apply-Templates element uses the Select attribute to define which templates to execute (based on which nodes are returned in the Select attribute XPath value). If you exclude the Select element, the Transformation engine will attempt to execute a template for each DIRECT descendent of the context node (the one matched by the currently executing template).
In the following example, we are going to implicitly execute the "toes" template. Then, from within that template, we are going to explicitly execute the "toe" template for each of the toe child nodes:
Launch code in new window » Download code as text file »
This results in the output: OPEN-TOES TOE TOE TOE TOE TOE CLOSE-TOES. Notice that OPEN-TOES appears once, then the Apply-Templates element selects all 5 toe nodes, executing the "toe" template for each node (based on the template Match attribute), and then outputs the CLOSE-TOES text.
The Select attribute of the Apply-Templates node doesn't have to select a direct descendent of the context node. It doesn't even have to select any descendent of the context node. All it has to have is a valid XPath value. In the following example, we will select all name nodes of the XML document:
Launch code in new window » Download code as text file »
Using this transformation, we get the following output: OPEN-TOES NAME NAME NAME NAME NAME CLOSE-TOES.
The Select attribute doesn't just have to match Element nodes; it can also match attribute nodes. Here, we are going to match all IsCute attributes of the descendent toe nodes:
Launch code in new window » Download code as text file »
Notice that our Apply-Templates' Select attribute is using an XPath path that selected an attribute node. Also, notice that our Template element matches on the attribute node, @iscute. Running the above transformation, we get the following output: A CUTIE!
Using the Template and Apply-Templates elements, we can apply templates to nodes within the given ColdFusion XML document during the XSL transformation, but it is through the use of the Value-Of element that we can actually reference values within the XML data. Using Value-Of, we can output the value of an XML node, wether it is a text node or attribute node (and I am sure other node types work as well).
It has two attributes: Select and Disable-Output-Escaping. The Select attribute uses XPath to determine which node value to get. The Disable-Output-Escaping attribute turns off the escaping of special characters. This is especially useful when we are outputting CDATA values. By default, the Transformation engine will escape special values like "<" and ">"; when outputting CDATA (or node data in general), we want those value to come through as-is so that XHTML can be stored in an XML document and therefore can set the Disable-Output-Escaping value to "yes".
In the following example, we are going to implicitly match all the toe nodes. Once inside the toe node, we are going to output the name of the toe: the text node within the nested name element node.
Launch code in new window » Download code as text file »
This gives us the output: Christina Julia Maria Kim Kit.
Just like the Apply-Templates element, the Value-Of Select attribute does not have to be relevant to the context node. By default, it is relevant to the context node, but it could certainly use an XPath path that selects from anywhere in the current XML document.
If you want to select an attribute value, all you would need to do is use an XPath path to select the attribute node. The following code will output the @IsBigToe attribute value.
Launch code in new window » Download code as text file »
Performing this transformation, we get the following output: [true] [] [] [] []. I have included the brackets to demonstrate that while the toe template still matches all 5 toes, the Value-Of only gets one value. Notice that is does NOT throw an error if the context node in question does NOT have the desired attribute. Value-Of always returns a string; this string might be empty a lot of the time.
The For-Each element allows us to loop over the selected nodes. This is like applying a template to a set of nodes without having to leave the currently executing template. The For-Each element has only one attribute - Select - which defines the node set over which we will iterate.
In this example, we are going to apply a toes template. Then, from within that template, we are going to loop over each toe node and output the name of the toe.
Launch code in new window » Download code as text file »
Running this, we get the following output: [Christina] [Julia] [Maria] [Kim] [Kit]. For each iteration of the For-Each loop, the Transformation engine takes the current "toe" node and makes it the context node.
The If element allows us to run code based on conditions. The If element has only one attribute, test, which must evaluate to true or false. What is considered True can be a bit confusing. It can be any valid XPath or expression such a function return or even the existence of a nested element. For example, in the following code, we are only going to show the name of toes that actually have a nested name element.
Launch code in new window » Download code as text file »
Here, as we are looping over the toes, we use the test condition "name" to make sure the context toe has a nested name element. If it does, we then execute that transformation code within the If element.
XSLT does not allow the ELSE part of a traditional IF-ELSE condition. Furthermore, it has no ElseIF statement. To perform this kind of conditional testing, we have to use the XSLT equivalent of a Switch statement which uses the Choose, When, and Otherwise elements.
Here, we are going to apply a template for each toe node and describe the toe. The description of the toe will be based on the existence of certain attributes:
Launch code in new window » Download code as text file »
Running the above transformation, we get the following output: Big! N/A N/A N/A Cute!
White space output that is generated during an XSL transformation can be a little confusing. I don't fully understand the rules. Sometimes it outputs white space, sometimes it does not. I think it has to do with a combination of which XSLT elements you are using and what text literals you are using. You can use the Text element to make sure all characters get output as-is in the resultant transformation.
The Text element has one attribute: Disable-Output-Escaping. Like the Value-Of element, this attribute flags whether or not to escape special output characters (defaults to "no"). Other than that, it simply outputs the text contained within its open and close tags.
If we run this transformation:
Launch code in new window » Download code as text file »
We get the following output: ChristinaJuliaMariaKimKit. Notice that none of the white space between the XSLT elements was carried through. Now, we can modify that to add explicit white space:
Launch code in new window » Download code as text file »
Here, we are adding a dash between elements and we get the following output: Christina - Julia - Maria - Kim - Kit -.
In ColdFusion, it is easy to build dynamic XHTML elements; all you have to do is put CFIF tags within the XHTML tag markup. This kind of thing cannot be done quite as easily in XSLT. Instead of putting conditional statements inside the markup of a tag, you can use the Element and Attribute elements to build nodes one an attribute at a time.
For example, if we wanted to convert the toes XML node to an XHTML unordered list (OL), including the attributes, we would have to do something like this:
Launch code in new window » Download code as text file »
As you can see, building a dynamic XHTML element is no easy task in XSL transformations.
So there you go. I am not sure how I feel about XSL Transformation just yet. There is certainly some cool stuff here, but I am not sure if the overhead of its complexity makes it worth while; I feel like many of the things it does can be more easily done in ColdFusion. I think the real power here lies in the Template matching - it has some very nice recursive possibilities. I think now that I have a better understanding of XSLT, it will start to look like a solution in more places.
Download Code Snippet ZIP File
Comments (0) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
There are no comments posted for this web log entry.