As in XSLT 2.0, as far as I know (correct me if Im wrong), theres no built-in mechanism in the language for handling exceptions.
I have several stylesheets that are trying to do some processing on certain pieces of the input document, copying everything else unchanged. There are rare exceptional conditions that I cannot detect before I begin to make a conclusion for a given piece. They are rare enough that when I come across them, all I want to do is cancel the processing on this piece and fix it unchanged. Some exception handling is fine, but XSLT doesn't help much. I do not want to get Java or another language in the mix here.
I have a workable solution, described below, but I'm curious about other approaches. Do you have a better way to do something like this?
Here is an example of the scenario I'm talking about. Here is the input document:
<doc> <block>some text, just copy.</block> <table> <tr><td>a</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>a</td><td>c</td></tr> <tr><td>b</td><td>c</td><td>a</td></tr> </table> <block>some more text, just copy.</block> <table> <tr><td>a</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>a</td><td>x</td></tr> <tr><td>b</td><td>c</td><td>a</td></tr> </table> </doc>
I want to look at each table and replace all the values โโof cell โaโ with โB. However, if theres somewhere in the table, I just want to copy the table without changes. I know that in this case I could just do the tr/td[.='x'] test tr/td[.='x'] in the table to detect this condition. In the real case, however, it is not so simple to check in advance for the condition.
Here is some XSLT that does not take exception into account:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="table"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="inner"/> </xsl:copy> </xsl:template> <xsl:template mode="inner" match="td"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:choose> <xsl:when test=". = 'a'"> <xsl:value-of select="'B'"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> <xsl:template mode="inner" match="@*|node()" priority="-10"> <xsl:copy> <xsl:apply-templates mode="inner" select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="@*|node()" priority="-10"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Result:
<doc> <block>some text, just copy.</block> <table> <tr><td>B</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>B</td><td>c</td></tr> <tr><td>b</td><td>c</td><td>B</td></tr> </table> <block>some more text, just copy.</block> <table> <tr><td>B</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>B</td><td>x</td></tr> <tr><td>b</td><td>c</td><td>B</td></tr> </table> </doc>
This was done by substitutions in the second table, which I do not want.
My current solution is to do this:
- Extract each table to a variable, not directly to the output file
- If an exception occurs, emit the
<EXCEPTION/> - After processing each table, review the variable for the
<EXCEPTION/> . - If an exception occurs, copy the source table, otherwise copy the contents of the variable.
Has a modified code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="table"> <xsl:variable name="result"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="inner"/> </xsl:copy> </xsl:variable> <xsl:choose> <xsl:when test="$result//EXCEPTION"> <xsl:copy-of select="."/> </xsl:when> <xsl:otherwise> <xsl:copy-of select="$result"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template mode="inner" match="td"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:choose> <xsl:when test=". = 'a'"> <xsl:value-of select="'B'"/> </xsl:when> <xsl:when test=". = 'x'"> <EXCEPTION/> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> <xsl:template mode="inner" match="@*|node()" priority="-10"> <xsl:copy> <xsl:apply-templates mode="inner" select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="@*|node()" priority="-10"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
And the correct conclusion:
<doc> <block>some text, just copy.</block> <table> <tr><td>B</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>B</td><td>c</td></tr> <tr><td>b</td><td>c</td><td>B</td></tr> </table> <block>some more text, just copy.</block> <table> <tr><td>a</td><td>b</td><td>c</td></tr> <tr><td>b</td><td>a</td><td>x</td></tr> <tr><td>b</td><td>c</td><td>a</td></tr> </table> </doc>