You are on page 1of 31

Tip 1 Output of XSLT transformation does not necessarily have to be a well-formed XML document.

XSLT can be used to transform XML into any other text format, such as HTML, WML, CSV, text, and so on.

Tip 2 If you need to process the children of the element in the order that they appear, it is not necessary to write (without the <xslt:apply-templates select= /> for each children. Instead, just a single <xslt:apply-templates /> (without the select attribute) would work in this case. It tells the XSLT processor to apply templates to all the children elements under the current element, in the order they occur. For example, lets say in an XML document, the Book node has ISBN and Publisher as the children. In order to process both the child nodes in the same order they appear, instead of writing:

<xslt:template match=/Book> <xslt:apply-templates select=ISBN /> <xslt:apply-templates select=Publisher /> </xslt:template>


We can just write:

<xslt:template match=/Book> <xslt:apply-templates </xslt:template>


And get the same effect.

/>

Tip 3 Moded templates can be used to get different processing for the same node in different situations. Both xslt:template and xslt:apply-templates have an optional mode attribute. Remember that:

If xslt:template does not have a match attribute, it must not have a mode attribute, If an xslt:apply-templates element has a mode attribute, then it applies only to those template rules from xslt:template elements that have a mode attribute with the same value, and If an xslt:apply-templates element does not have a mode attribute, then it applies only to those template rules from xslt:template elements that do not have a mode attribute.

Tip 4 <xslt:copy-of> instruction can be used to reuse XML from the source document or from the result tree fragment. Unlike <xslt:value-of> (that converts fragment into a string before copying it into the result tree), the <xslt:copy-of> instruction instead copies the complete fragment based on the (required) select attribute, without first converting the fragment into a string . If the result of the select expression is a node-set, all the nodes in the set are copied in document order into the result tree; copying an element node copies the attribute nodes, namespace nodes and children of the element node as well as the element node itself.

Tip 5 <xslt:choose> can be used inside the <xslt:variable> element, to conditionally set the XSLT variable s value. For example:

<xslt:variable name=fontColor> <xslt:choose> <xslt:when test=@inStock=y>green</xslt:when> <xslt:otherwise>red</xslt:otherwise> </xslt:choose> </xslt:variable>


In the above example, depending on the value of inStock attribute in the source XML document, the fontColor variable s value would be either green or red.

Tip 6 The normalize-space XPath function can be used to to normalize a string (get rid of superfluous whitespace). This function returns a whitespace normalized string by stripping leading and trailing whitespace and replacing sequences of whitespace characters by a single space. Whitespace refers to one or more space (#x20) characters, carriage returns, line feeds, or tabs.

Tip 7 The | character (union operator) can be used to write multiple location path patterns and get a node-set on which the current operation is to be performed. For example, <xslt:apply-templates select=/Book/Author | /Article/Author/> returns a node-set containing Author element either inside Book node or under the Article node.

Tip 8 Avoid using http://www.w3.org/TR/WD-xsl namespace for the XSLT stylesheet documents. The earlier versions of MSXML (and hence Internet Explorer) supported this namespace, which corresponds to an early working draft. But this namespace is now outdated, and you should use the http://www.w3.org/1999/XSL/Transform namespace declaration as per the final XSLT 1.0 W3C Recommendation. Internet Explorer 6 installs MSXML 3.0 SP2 in replace mode and hence by default enabling the XSLT support in Internet Explorer. If you use the http://www.w3.org/1999/XSL/Transform namespace in your stylesheet and it does not work in Internet Explorer, the most probable cause is that it is using an earlier version of MSXML parser. You can download MSXML 3.0 and install it in replace mode (using xmlinst.exe) to make sure that Internet Explorer is using the correct version of MSXML parser that supports XSLT 1.0. See http://www.PerfectXML.com/msxmlFiles.asp for more details.

Tip 9 CSS can be used to style XML in the same way as you use it w ith HTML. Consider the following CSS file (test.css):

BookName { background-color:#EEEEEE; font-family:Verdana; font-size:1.2em; font-weight:bold; color:Maroon; margin-top:1em; display:list-item; } Author { font-family:Arial; font-size:1em; margin-left:2em; }
Now, the <?xml-stylesheet ...?> instruction with type=text/css can be used inside the XML file to refer to the above CSS file:

<?xml version=1.0 encoding=utf-8 ?> <?xml-stylesheet type=text/css href=test.css ?> <Books_XSLT>

<Book> <BookName>Beginning XSLT</BookName> <Author>Jeni Tennison</Author> </Book> <Book> <BookName>XSLT Programmers Reference, 2nd Edition</BookName> <Author>Michael Kay</Author> </Book> </Books_XSLT>
The above XML file (test.xml) refers to the test.css to style it when viewed in a XML+CSS aware browser. If you do NOT need to really transform the XML document, but just need to present in a XML+CSS aware browser, you can use the solution such as one described above.

Tip 10 Use data-type=number attribute with <xslt:sort> if the values by which the nodes are being sorted are numeric.

Tip 11 The -t command line option can be used with Instant SAXON or msxsl.exe to see the load and transformation timings (useful for performance testing).

Similarly, the -DIAG option can be used with Xalan to get the time required to apply the transformation.

Also see: CatchXSL! from www.xslprofiler.org .

Tip 12 The position() and last() XPath functions can be used together to find out if the current context node is the last node or not. For example: <xslt:if test=position() != last()>; </xslt:if>

Tip 13 A recursive template is a named template that calls itself. For example:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0> <xslt:output method=text omit-xml-declaration=yes /> <xslt:template match=/> <xslt:call-template name=ratings> <xslt:with-param name=cntStars select=5/> </xslt:call-template> </xslt:template> <xslt:template name=ratings> <xslt:param name=cntStars /> <xslt:if test=$cntStars> <xslt:text>*</xslt:text> <xslt:call-template name=ratings> <xslt:with-param name=cntStars select=$cntStars - 1 /> </xslt:call-template> </xslt:if> </xslt:template> </xslt:stylesheet>
In the above stylesheet the template named ratings calls itself; hence it is a recursive template. If you apply the above XSLT transformation on any well-formed XML, youll see the 5 asterisks as the output (as the cntStars is 5 when the first time template is called in the above stylesheet).

Tip 14 The document() XSLT function can be used to access information from an external source that returns XML. For example, lets say we have a source XML document that looks like:

<?xml version=1.0?> <Book ISBN=186100589X />


The following stylesheet uses the ISBN attribute from the above (source) XML document, and gets the books Amazon.com sales rank by calling an external XML document using the document() function (actually it calls the SalesRankNPrice Web service.)

<?xml version=1.0?> <xslt:stylesheet

xmlns:xslt=http://www.w3.org/1999/XSL/Transform xmlns:pxml=http://www.PerfectXML.com/NETWebSvcs/BookService version=1.0> <xslt:output method=text omit-xml-declaration=yes /> <xslt:template match=/> <xslt:variable name=URL select=concat( http://www.PerfectXML.net/WebServices/SalesRankNPrice/BookService.asmx/GetAmazonSalesRank?ISBN=, Book/@ISBN) /> <xslt:variable name=SalesRank select=document($URL)/pxml:string /> Sales rank for Book <xslt:value-of select=Book/@ISBN /> <xslt:text> :</xslt:text> <xslt:value-of select=$SalesRank /> </xslt:template> </xslt:stylesheet>
When the above stylesheet is applied on the source XML document, it generates the (dynamic) output similar to:

Sales rank for Book 186100589X : 3,221


The above stylesheet creates a local variable named URL and initializes its value with the concatenated string that contains the URL to send the GET request to the SalesRankNPrice Web service (ex: http://www.PerfectXML.net/WebServices/SalesRankNPrice/BookService.asmx/GetAmazonSalesRank?ISBN=186100589X). The $URL is then passed to the document() function which loads the external XML document (returned by the Web service method call), extracts the string elements value and saves into a variable named SalesRank. Finally we print the output message using the SalesRank variable and Book/@ISBNattribute. As the returned XML uses default namespace, the <xslt:stylesheet > element declares that namespace, along with the namespace prefix (pxml), which is then used in the XPath expression below in the stylesheet.

Tip 15 function-available() function can be used to check whether the XSLT processor being used supports a particular extension function.

For example, SAXON supports a function called as evaluate that can be used to evaluate dynamically generated XPath expressions; and as of MSXML 4.0, it does not support any function with this name. Consider the following test XML document:

<?xml version=1.0?> <ROOT> <Book> <Author>Darshan Singh</Author> </Book> <Article> <Author>Jay Y</Author> </Article> </ROOT>
And the XSLT stylesheet

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0> <xslt:param name=paramExpr /> <xslt:output method=text omit-xml-declaration=yes /> <xslt:template match=/ xmlns:saxon=http://icl.com/saxon> <xslt:choose> <xslt:when test=function-available(saxon:evaluate)> <xslt:value-of select=saxon:evaluate($paramExpr) /> </xslt:when> <xslt:otherwise> <xslt:text>Method not supported by this processor!</xslt:text> </xslt:otherwise> </xslt:choose> </xslt:template> </xslt:stylesheet>
The above stylesheet uses function-available() XSLT function to make sure that the processor being used supports evaluate function; if it does we print the result of the XPath expression passed as the parameter to the stylesheet; if the processor does not support the evaluate function, we print the appropriate message.

Here is the result:

Tip 16 With MSXML, we can define our own custom extension functions using the <msxsl:script> top-level element, and then call (use) these functions inside the stylesheet. For example, consider the following stylesheet:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform xmlns:msxsl=urn:schemas-microsoft-com:xslt xmlns:ext=http://www.PerfectXML.com/ version=1.0> <xslt:output method=text omit-xml-declaration=yes /> <msxsl:script language=JScript implements-prefix=ext> function js_Escape(strParamValue) { return encodeURIComponent(strParamValue); } </msxsl:script> <xslt:template match=/> <xslt:value-of select=ext:js_Escape(I like C++)/> </xslt:template> </xslt:stylesheet>
When you apply the above stylesheet on any source XML document, the output would be:

I%20like%20C%2B%2B

Related links:
PRB: Cannot Unload Assemblies That You Create and Load by Using Script in XSLT INFO: Roadmap for Executing XSLT Transformations in .NET Applications

Tip 17 XSLT surely can do a lot more than what CSS could do; but XSLT does NOT necessarily replace or compete with CSS. CSS is well supported by the current browsers and works very well with HTML. XSLT and CSS can be infact used together, for example, XSLT to convert XML to HTML and then CSS to present the HTML in the browser and separate the presentation details in a separate CSS stylesheet.

Tip 18 A whitespace -separated list of namespace prefixes can be specified with the xslt:exclude-result-prefixes (optional) attribute of the <xslt:stylesheet> element, to avoid copying the specified namespaces declarations from the stylesheet to the result tree. Use #default to indicate the default namespace. For example, consider the following source XML document

<?xml version=1.0?> <ADOResults xmlns=#RowsetSchema xmlns:rs=urn:schemas-microsoft-com:rowset > <rs:data > <row CustomerID=ALFKI CompanyName=Alfreds Futterkiste/> </rs:data> </ADOResults>
And here is the XSLT stylesheet:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 xmlns:rs=urn:schemas-microsoft-com:rowset xmlns:z=#RowsetSchema exclude-result-prefixes=rs z #default > <xslt:output method=xml /> <xslt:template match=/> <Text>

<xslt:value-of select=//rs:data/z:row/@CustomerID /> <xslt:text>: </xslt:text> <xslt:value-of select=//rs:data/z:row/@CompanyName /> </Text> </xslt:template> </xslt:stylesheet>
Apply the above stylesheet on the source XML document and youll see that the <Text> node in the result XML does not have any XML namespace declarations. Remove the exclude-result-prefixes=rs z #default attribute from the stylesheet, apply the stylesheet again, and notice that the generated XML now contains <Text> node with the namespace declarations.

Tip 19 XSLT 1.0 does NOT allow conditionally including (<xslt:include>) other stylesheets. The workarounds include using moded templates or dynamically generating the stylesheet. In addition to this, there are some other things that XSLT does not allow: This includes calling a template whose name is decided at run-time, apply-templates with a mode decided at run-time, deciding the sort key at run-time, checking if the external document exist before calling the document() function. In some cases extension funcations can be used to workaround these limitations. XSLT 1.0 (and XPath 1.0) does not provide any functions for regular expressions processing. And finally, there is no way to produce multiple output documents as a result of a transformation, as per XSLT 1.0.

Tip 20 XSLT 1.0 defines three built-in template rules that automatically get imported into the stylesheet and hence they have lower import precedence than all other template rules, allowing us to override a built-in template rule by including an explicit template rule. The built-in template rules are:

<xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0> <!-- For any mode --> <xslt:template match=*|/> <xslt:apply-templates /> <!-- For any mode --> </xslt:template> <!-- For any mode --> <xslt:template match=text()|@*> <xslt:value-of select=./> </xslt:template> <!-- For any mode -->

<xslt:template match=processing-instruction()|comment() </xslt:stylesheet>

/>

Tip 21 When there are more than one matching template patterns for the current node being processed, to resolve the conflicts between the templates, XSLT processor makes use of the template priorities. Template with the highest priority wins and gets executed. We can explicitly specify the priorityattribute value with the <xslt:template > element, else the processor will use the default priorities as per the following table. Note that the all the templates with the lower precedence (such as imported templates) are first eliminated when resolving the conflicts.
Pattern type Default Priority Examples

Node test by type

-0.50

* node() text() @* child::* ns1:* child::ns1:* Customer ns1:Order child::ns2:Author @uniqueID /Customr/FName ns1:Book[@ISBN=123456] //ns1:Config/ns2:maxErrors /Book/Author | /Article/Author child::text() | @* @* | *

NCName (namespace):* QName or Processing instruction tests by literal

-0.25 0.00

Everything else

0.50

Multiple Patterns (using | union operator)

treated equivalently to a set of template rules, one for each alternative

Even after using the above priority scheme, if there are still two templates with the same import precedence and priority, the XSLT processor can either choose the one that occurs last in the stylesheet, or report an error. SAXON in this case reports a recoverable error message and then chooses the template that occurs last in the stylesheet; on the other hand, MSXML and .NET also chooses the template that occurs last in the stylesheet, but does not show any errors or warnings.

If you know that there can be multiple matching templates that can match the same node, it is better to not rely on the default priorities, but explicitly specifying the priorities with each such template.

Tip 22 The disable-output-escaping attribute with a value of yes can be used on <xslt:text> or <xslt:value-of> elements to avoid escaping the special character (if present any) in the output text nodes being created. The disable-output-escaping attribute may be used with the html output method as well as with the xml output method. The text output method ignores the disable-output-escaping attribute, since it does not perform any output escaping. However, since disabling output escaping may not work with all XSLT processors and can result in XML that is not well-formed, it should be used only when there is no alternative.

Tip 23 Use indent=no attribute with the <xslt:output > instruction to avoid adding extra whitespace in the output document (especially with HTML) to create smaller output files that download faster. And in addition, it is usually not safe to use indent=yes with document types that include element types with mixed content. The default value of indent attribute for html output type is yes, and default value of indent attribute for xml output is no.

Tip 24 XSLT stylesheet is a well-formed XML document. Like XML documents, XSLT stylesheet also can be generated dynamically. Infact, XSLT stylesheet(s) can be written to generate other XSLT stylesheets.

Tip 25 Sometimes the XPath expressions get tricky! For example, in XPath, value1 != value2 and not (value1 = value2) are different! Consider following example: Source XML document:

<?xml version=1.0?> <Doctors> <Doctor inPlan=Y>Dr. Yes</Doctor> <Doctor inPlan=N>Dr. No</Doctor> <Doctor>Dr. Unknown</Doctor> </Doctors>
And the XSLT stylesheet:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:template match=/> <xslt:for-each select=//Doctor[@inPlan != N]> <xslt:value-of select=. /> </xslt:for-each> <!-<xslt:for-each select=//Doctor[not (@inPlan = N)]> <xslt:value-of select=. /> </xslt:for-each> --> </xslt:template> </xslt:stylesheet>
Apply the above stylesheet and you should see just Dr. Yes as the output. This is because //Doctor[@inPlan != N] excludes the Doctor nodes that do not have inPlan attribute. In other words, the condition means return all doctors having the inPlan attribute and with value not equals N. Comment out the first <xslt:for-each > and uncomment the second <xslt:for-each > and apply the stylesheet. This time the output would be Dr. Yes Dr. Unknown. This is because //Doctor[not (@inPlan = N)] means return all doctors that do not have any attribute named inPlan attribute whose value is N, and this includes even the nodes that do not have inPlan attribute, and hence the output contains Dr. Unknown.

Tip 26 <xslt:sort > XSLT element and position() and last() XPath functions can be used together to find the biggest and smallest number. Consider the following source XML document:

<?xml version=1.0?> <Store> <Book CopiesSold=2837>Book A</Book> <Book CopiesSold=982>Book B</Book> <Book CopiesSold=10872>Book C</Book> <Book CopiesSold=200>Book D</Book> </Store>
And the XSLT stylesheet:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:template match=/> <xslt:for-each select=//Book> <xslt:sort select=@CopiesSold data-type=number /> <xslt:if test=position()=1> Book with lowest sale: <xslt:value-of select=./> </xslt:if> <xslt:if test=position()=last()> Book with highest sale: <xslt:value-of select=./> </xslt:if> </xslt:for-each> </xslt:template> </xslt:stylesheet>
The output of transformation is:

Book with lowest sale: Book D Book with highest sale: Book C

Tip 27 The <xslt:comment> element can be used to output comments in HTML. Example:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=html indent=no /> <xslt:template match=/> <xslt:comment> My First Stylesheet</xslt:comment>

</xslt:template> </xslt:stylesheet>
Will produce the following output:

<!-- My First Stylesheet-->

Tip 28 Attribute Value Template (AVT) is a handy technique to dynamically generate the value of an attribute. AVT referes to an expression inside curly brackets ({}). Consider the source XML document:

<?xml version=1.0?> <Email>darshan@PerfectXML.com</Email>


And the XSLT stylesheet:

<?xml version=1.0?> <xslt:stylesheet xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=html indent=no /> <xslt:template match=/> <a href=mailto:{Email}><xslt:value-of select=Email/></a> </xslt:template> </xslt:stylesheet>
Note how the href attribute in the above stylesheet uses the AVT. In this case {Email} would be replaced with the value of the expression, that is value of the Email node, producing following output:

<a href=mailto:darshan@PerfectXML.com>darshan@PerfectXML.com</a>

Separate pairs of curly brackets can be used to insert multiple AVTs. To insert curly bracket literally into an attribute s value, then write two opening and closing curly brackets (example: <data type={{private}} /> in the XSLT stylesheet would produce <data type={private} ></data> as the output.

Tip 29 In addition to the four basic data types (string, number, boolean, and node-set) available in XPath 1.0, the XSLT 1.0 specification introduced a fifth type known as result tree fragments (RTF). The <xslt:variable> and <xslt:param> elements (known as variable-binding elements) can be used to assign values to variables and parameters. Both the elements support the optional select attribute that can be used to set the variable or parameter to one of the basic XPath types (mentioned above). The second alternative is to use the content within the <xslt:variable>...</xslt:variable>, <xslt:param>...</xslt:param>, or <xslt:withparam>...</xslt:with-param> element, and this method allows to set the variable or parameters value to a result tree fragment (the fifth XSLT type mentioned above). Either the tree text can be (statically) written directly between the tags or template body (such as <xslt:apply-templates>) can be called to dynamically create the result tree fragment. However, once the result tree fragment is constructed, it is not possible to use XPath expressions on the tree, the reasons being XPath 1.0 does not know about the result tree fragment type, RTF is a string not a node set, and finally as the result tree fragment does not necessarily have to be a wellformed; it just needs to be well-balanced. To overcome this limitation in XSLT 1.0, XSLT Processors introduced an extension function called as node-set() that allows result tree fragment to be used as XPath node set.

msxsl:node-set() (as in MSXML) exsl:node-set (as in 4XSLT, jd.xslt, SAXON, and libxslt) xalan:nodeset (as in Apache Xalan)

The XSLT 2.0 Working Draft removes the result tree fragment type and the temporary trees, which is a true node-set, can be constructed using xslt:variable, xslt:param, xslt:with-param, or xslt:result elements. This eliminates the need to use the node-set() extenstion function.

Tip 30 XSLT 2.0 Working Draft specification supports creating multiple output documents using the xslt:result-document element. For example, lets say when a Web form is submitted, we create an XML document that contains name and values of the data elements posted via the Web form. Now, when this XML data is received on the server, a single XSLT stylesheet (that uses xslt:result-document ) can now be used to create two output documents: first an HTML text that needs to be sent to the browser client, second a text or XML file that need to be saved on the server containing the posted data. Another example would be that, a single XSTL document can be used to transform source XML document into formats such as WML, VoiceXML, XHTML, XML, Text, SVG, HTML, etc.; instead of writing different multiple stylesheet for each output.

Tip 31 The XSLT 2.0 Working Draft introduces better support for grouping via the xslt:for-each-group element. Click here to see an example of how this new element simplifies the grouping in XSLT. You can use Michael Kays SAXON XSLT processor to try out various features defined in the XSLT 2.0 and XPath 2.0 working drafts.

Tip 32 The XSLT 2.0 Working Draft allows user-defined XSLT functions to be written that can be called from any XPath expression used in the stylesheet. These custom stylesheet functions can be defined using <xslt:function> element. The content of the xslt:function element consists of zero or more xslt:param elements that specify the formal arguments of the function, followed by zero or more xslt:variable elements that can be used to compute intermediate results, followed by a mandatory xslt:result element that defines the value to be returned by the function. It is also possible to include xslt:message elements after the xslt:param elements and before the xslt:result, to provide diagnostic output.

Tip 33 The XSLT 2.0 Working Draft introduces a new method, unparsed-text that can be used to read external file(s). The result of this function is a sequence of strings, containing one string for each URI in the sequence supplied as the first argument.

Tip 34 The document() function, which in XSLT 1.0 was an additional function defined in the XSLT specification, is now a core function defined in XPath 2.0.

Tip 35 The attribute value template can be used as the value of name attribute on the <xslt:element> instruction to dynamically set the name of the element being created. For example:

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:template match=/> <ROOT>

<xslt:for-each select=child::*/child::*> <xslt:element name=row{position()} /> </xslt:for-each> </ROOT> </xslt:template> </xslt:transform>


The above stylesheet creates element named row1, row2, and so on, for each child of the document element in the source XML document. Note how the AVT is used inside the name attribute in the <xslt:element> instruction.

Tip 36 The <xslt:attribute> elements can be wrapped inside <xslt:if> or <xslt:choose> to conditionally add attributes to the elements. For example, consider the following source XML document:

<Emp FName=John Middle= LName=Parra />


Lets say the requirement is transform Emp to an element named Employeeand only add attributes if it has some value

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:template match=/> <Employee> <xslt:for-each select=child::*/@*> <xslt:if test=string(.)> <xslt:attribute name={name()}><xslt:value-of select=. /></xslt:attribute> </xslt:if> </xslt:for-each> </Employee> </xslt:template> </xslt:transform>
The above stylesheet first creates an element named Employee and then for each attribute in the document element (Emp), we use the xslt:if instruction with string(.) as the condition to see if the attribute has any value (and that the attribute value string is not empty), in such cases only we create an attribute using the xslt:attribute instruction. Also note the use of AVT (attribute value template, {}) to get the attribute name and create an attribute with the same name.

Tip 37 The cdata-section-elements attribute of the <xslt:output> instruction can be used to list the elements whose text node children should be wrapped in CDATA sections.

Tip 38 We can have multiple <xslt:sort> instruction within <xslt:apply-templates> or <xslt:for-each> element; each subsequent <xslt:sort> is a subsort of the previous <xslt:sort>. For example, consider the following source XML document:

<Employees> <Department name=Marketing> <Emp name=Steven Buchanan salary=3000 /> <Emp name=Anne Dodsworth salary=5000 /> </Department> <Department name=Support> <Emp name=Andrew Fuller salary=4000 /> <Emp name=Laura Callahan salary=6000 /> </Department> <Department name=Development> <Emp name=Margaret Peacock salary=4000 /> <Emp name=Janet Leverling salary=6000 /> </Department> </Employees>
The goal is find out total expenses by department, sorted on department-wise expenses (descending order) and if two departments have the same total expense, we would like to sort on the department name (ascending order). Consider the following stylesheet:

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:template match=/> <Expenses> <xslt:for-each select=Employees/Department> <xslt:sort select=sum(Emp/@salary)

data-type=number order=descending /> <xslt:sort select=@name order=ascending /> <Department name={@name}> <xslt:value-of select=sum(Emp/@salary)/> </Department> </xslt:for-each> </Expenses> </xslt:template> </xslt:transform>
Note that the above stylesheet contains two xslt:sort elements within the xslt:for-each element, allowing us to first sort on total employee salaries (descending order) and then on department name (ascending order, if the total salaries are equal). The Development and Support department have the same total expenses, the Development department node appears after the Support department node in the source XML but still the output contains the Development node first (as shown below).

<?xml version=1.0 encoding=utf-16 ?> <Expenses> <Department name=Development>10000</Department> <Department name=Support>10000</Department> <Department name=Marketing>8000</Department> </Expenses>
Comment the <xslt:sort select=@name order=ascending /> line in the stylesheet and notice the difference in the output. The results shows that the nodes are processed in the document order (Support department appears before the Development department).

Tip 39 The select attribute in the xslt:sort element can take an XPath expression, allowing us to dynamically choose the attribute or element on which the data needs to be sorted. For example, consider the following source XML document:

<Employees> <Emp fname=Steven lname=Buchanan salary=500 /> <Emp fname=Anne lname=Dodsworth salary=5000 />

<Emp <Emp <Emp <Emp </Employees>

fname=Andrew lname=Fuller salary=4000 /> fname=Laura lname=Callahan salary=6000 /> fname=Margaret lname=Peacock salary=4000 /> fname=Janet lname=Leverling salary=6000 />

The goal is to write a stylesheet that accepts a parameter, and based on this parameter the employee records (above) need to be sorted.

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:param name=orderby /> <xslt:template match=/> <xslt:for-each select=Employees/Emp> <xslt:sort select=@fname[$orderby = fname] | @lname[$orderby = lname]/> <xslt:value-of select=@fname /> <xslt:text>, </xslt:text> <xslt:value-of select=@lname /> <xslt:text>, </xslt:text> <xslt:value-of select=@salary /> <xslt:text> </xslt:text> </xslt:for-each> </xslt:template> </xslt:transform>
The above transformation document takes orderby parameter, and note how it is used in the select attribute of the xslt:sort element. It sorts on @fname if the orderby parameter is fname and it sorts on @lname if the orderby parameter is lname. If you pass fname as the parameter value, you should see the following output:

Andrew, Fuller, 4000 Anne, Dodsworth, 5000 Janet, Leverling, 6000 Laura, Callahan, 6000 Margaret, Peacock, 4000 Steven, Buchanan, 500
As an exercise, update the above stylesheet (or re-write it) to allow sorting on salary attribute also, making sure it correctly sorts in numerical order.

Lets see how much time it takes for you to implement this! :)

Tip 40 If an empty string is passed as the parameter to the XSLT document() function, it returns root node of the stylesheet itself. This trick can be useful sometime. Consider the following transformation example:

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:template match=/> Output method for this transformation is <xslt:value-of select=document()//xslt:output/@method />. </xslt:template> </xslt:transform>
The document() function call returns the root node of the above (self) stylesheet and the predicate then selects the value of method attribute on the xslt:output element. With the current code, it would return

Output method for this transformation is text.


Change the value of method attribute on the xslt:output element to either xml or html, and run the transformation (over any source XML document) again and notice the output.

Tip 41 The xslt:message instruction can be used to report an error condition. terminate = yes attribute value can be used in the xslt:message instruction to tell the XSLT processor to report the message (usually in a critical error condition) and quit. The default value for the terminate attribute is no. Keep the default value (dont write terminate attribute) and try out xslt:message with various XSLT processors, especially MSXML, SAXON and Xalan and notice the difference in the results.

Tip 42 The system-property function can be used to find out the information about the XSLT Processor. The details such as version of XSLT standard implemented by the processor, the vendor n ame and vendor URL can be accessed using this function. For example: Consider the following XSLT transformation document

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:template match=/> <xslt:value-of select=system-property(xslt:version) /> <xslt:text > </xslt:text><xslt:value-of select=system-property(xslt:vendor) /> <xslt:text > </xslt:text><xslt:value-of select=system-property(xslt:vendor-url) /> </xslt:template> </xslt:transform>
The above stylesheet is when applied on any source XML document, the following output is generated: With MSXML

1 Microsoft http://www.microsoft.com
With Instant SAXON

1 SAXON 6.5.2 from Michael Kay http://saxon.sf.net/


With Xalan

1 Apache Software Foundation http://xml.apache.org/xalan-j

Tip 43 The normalize-space(.) function can be used in conjunction with XSLT conditional instructions (xslt:if or xslt:when) to check if the current element is empty or not. For example, consider the following XML document

<Magazines> <Row>eWeek</Row> <Row> </Row> <Row>Computer World</Row> <Row></Row> <Row> </Row> <Row>MSDN Magazine</Row> <Row>XML Journal</Row> </Magazines>
Note that the empty element contain some whitespace (newlines, tabs, etc.) And the XSLT transformation document:

<xslt:transform xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0 > <xslt:output method=text /> <xslt:variable name=icntEmptyRows select=0 /> <xslt:template match=/> <xslt:for-each select=/Magazines/Row> <xslt:choose > <xslt:when test=normalize-space(.)> </xslt:when> <xslt:otherwise> Row <xslt:value-of select=position() /> is an empty row. </xslt:otherwise> </xslt:choose> </xslt:for-each> </xslt:template> </xslt:transform>
Apply this stylesheet on the source XML document shown above, and youll see the output as:

Row 2 is an empty row. Row 4 is an empty row. Row 5 is an empty row.

Tip 44
XPath 2.0 introduces many new functions (such as xf:distinct(), xf:current-dateTime, xf:string-pad, xf:tokenize, xf:get-local-name-from-

QName, xf:deep-equal, and many more such functions) that really simplify locating information and processing data in an XML document.

Tip 45 You can use XSL to XSLT Converter to updates Microsoft Internet Explorer 5 XSL style sheets to XSLT-compliant style sheets. It is also available here .

Tip 46 In addition to the external XSLT transformation document that can be applied to the XML document, it is also possible to embed XSLT stylesheet content inside the XML document and apply it. Consider the following XML document:

<?xml version=1.0 encoding=UTF-8?> <?xml-stylesheet type=text/xml href=#tableXSLT?> <!DOCTYPE doc [ <!ATTLIST xslt:stylesheet id ID #REQUIRED> ]> <Magazines> <Row>eWeek</Row> <Row>Computer World</Row> <Row>MSDN Magazine</Row> <Row>XML Journal</Row> <xslt:stylesheet id=tableXSLT xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0> <!--to ignore the stylesheet itself when transforming--> <xslt:template match=xslt:stylesheet /> <!-- do nothing--> <xslt:template match=/> <html>

<head /> <body> <table> <xslt:for-each select=//Magazines/Row> <tr><td> <xslt:value-of select=. /> </td></tr> </xslt:for-each> </table> </body> </html> </xslt:template> </xslt:stylesheet> </Magazines>
Pass the above XML document along with the -a parameter to SAXON and you should see the transformed HTML document. Note that not all XSLT processors support the above scheme.

Click here for further details on this.

Tip 47 On the server side, the alternative to writing code that on-the-fly applies XSLT transformation, is to use one of the following solutions :

Use AxKit. See www.axkit.org for details. Use Cocoon. See xml.apache.org/cocoon for details. Use XSQL. See Oracle Technology Network Web site for details. Use XSLT ISAPI Filter for IIS. See www.xsltfilter.com for details. Use XSL ISAPI Filter 2.2 from Microsoft. See MSDN for details.

Tip 48 XML elements can be dynamically created using xslt:element instruction, attributes can be created using xslt:attribute instruction, text nodes can be created using xslt:value-of or xslt:text instructions, comments can be created using xslt:comment instruction, processing instructions (PIs) can be created by using xslt:processing-instruction element, and CDATA sections can be created by using cdata-section-elements attribute in the xslt:output instruction.

Tip 49 XSLT variable and conditional processing statement (xslt:if or xslt:choose) can be used together to dynamically decide the sort order (ascending or descending). Consider the following XML document

<Books order=a> <book rating=2>A</book> <book rating=5>B</book> <book rating=4>C</book> <book rating=5>D</book> <book rating=3>E</book> </Books>
And the XSLT stylesheet:

<xslt:stylesheet id=tableXSLT xmlns:xslt=http://www.w3.org/1999/XSL/Transform version=1.0> <xslt:output method=text/> <xslt:template match=/> <xslt:variable name=order > <xslt:choose> <xslt:when test=/Books/@order=a >ascending</xslt:when> <xslt:otherwise>descending</xslt:otherwise> </xslt:choose> </xslt:variable> <xslt:for-each select=/Books/book> <xslt:sort select=@rating data-type=number

order={$order} /> <xslt:value-of select=. /><xslt:text> </xslt:text> </xslt:for-each> </xslt:template> </xslt:stylesheet>


Now, depending on the value of the order attribute in the source XML document, either the result would be sorted in ascending order (if the attribute is present and has the value of a) or in descending order (if the order attribute is omitted in the source XML document, of if present has the value of anything other than a).

Tip 50 This is a .NET tip: The XslTransform class in the System.Xml.Xsl namespace is an XSLT Processor implementing the XSLT Version 1.0 recommendation. Click here for some examples of using this class to apply XSLT transformations.

You might also like