perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
You are here: home »» Info Bank »» Articles » 50 XSLT Tips Saturday, 23 February 2008
50 XSLT Tips
by: Darshan Singh (Managing Editor, perfectxml.com)

PDF version of this page (703 KB)

perfectxml.com Recommends
Visual XSLT Complete XSLT development environment for Visual Studio .NET Visual XSLT
Complete XSLT development environment for Visual Studio .NET


Visual XSLT is the XSLT development plug-in for Visual Studio .NET, providing a powerhouse XSLT environment that ensures higher quality code and shortened development time.



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, let's 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 with 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 Programmer's 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).

Instant SAXON 6.5.2

MSXSL.exe

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

Xalan 2.4.0

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, you'll 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, let's 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 element's 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 you'll 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::*
NCName (namespace):* -0.25 ns1:*
child::ns1:*
QName or Processing instruction tests by literal 0.00 Customer
ns1:Order
child::ns2:Author
@uniqueID
Everything else 0.50 /Customr/FName
ns1:Book[@ISBN='123456']
//ns1:Config/ns2:maxErrors
Multiple Patterns (using | union operator) treated equivalently to a set of template rules, one for each alternative /Book/Author | /Article/Author
child::text() | @*
@* | *


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:with-param>...</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 well-formed; 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.
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, let's 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 Kay's 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" />

Let's 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 fname="Andrew" lname="Fuller" salary="4000" />
	<Emp fname="Laura" lname="Callahan" salary="6000" />
	<Emp fname="Margaret" lname="Peacock" salary="4000" />
	<Emp fname="Janet" lname="Leverling" salary="6000" />
</Employees>

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. Let's 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 (don't 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 name 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 you'll 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 :


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.

  Contact Us | E-mail Us | Site Guide | About PerfectXML | Advertise ©2004 perfectxml.com. All rights reserved. | Privacy