Two common scenarios for using recursion in XSLT are:
- When you have a set of repeating values in the source XML and you want the transformation result to reflect something about all of those values. For example, if you have a catalog of items in XML and want to present those items along with the price for all of the items, you would have to find that total price using a recursive template.
- When the source XML contains a number x in a tag, for example <countTo number="5"/>, and you want to present some information that same x number of times in the transformation output.
Suppose you have a product catalog:
<Products>
<Product>
<Name>Gadget</Name>
<Price>$10.00</Price>
</Product>
<Product>
<Name>Gizmo</Name>
<Price>$7.50</Price>
</Product>
...
</Products>
The goal is to add the first price in the list of products to the sum of all of the other prices until you get to the last price. You can do this with the XSL shown below:
<xsl:template name="priceSum">
<xsl:param name="productList"/>
<xsl:choose>
<xsl:when test="$productList">
<xsl:variable name="recursive_result">
<xsl:call-template name="priceSum">
<xsl:with-param name="productList"
select="$productList[position() > 1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of
select="number(substring-after($productList[1]/Price,'$'))
+ $recursive_result"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
In this solution you are calling the function on successively smaller lists of products, each time adding the first item in the list to the recursive call on the rest of the list. Once the list is empty, the termination condition is reached and you add zero to the result, which leaves the sum unchanged. This technique of working on node lists of smaller and smaller size can be useful in many settings.
The use of recursion in many XSLT engines has one major drawback when working with large datasets. It is all too easy to get a Stack Overflow or Out of Memory exception with recursive functions that reach a depth on the order of 10,000. If you are trying to sum prices on a catalog with more than 10,000 items, you may run into trouble.
Fortunately, there are ways to optimize recursive functions to reduce or even eliminate the depth of recursion incurred in a given template. The following postings introduce four methods to optimize how you write your recursive solutions in XSLT.