title: Techniques for XSLT exception handling
body: <p>As of XSLT 2.0, as far as I know (correct me if I’m wrong), there’s no native mechanism in the language for exception handling.</p>
  
  <p>I have some stylesheets that attempt to do some processing on specified chunks of an input document, copying everything else unaltered. There are rare exceptional conditions that I can’t easily detect before I start producing output for a given chunk. These are rare enough that when I encounter them, all I want to do is cancel processing on this chunk and emit it unaltered. Some sort of exception handling is in order, but XSLT doesn’t help very much. I don't want to get Java or another language into the mix here.</p>
  
  <p>I have a workable solution described below, but I'm wondering about other approaches. Do you all have a better way of doing something like this?</p>
  
  <p>Here’s an example of the sort of scenario I’m talking about. Here’s an input document:</p>
  
  <pre><code>&lt;doc&gt;
      &lt;block&gt;some text, just copy.&lt;/block&gt;
      &lt;!-- the following table should have B substituted for a --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
      &lt;block&gt;some more text, just copy.&lt;/block&gt;
      &lt;!-- the following table should be copied unaltered because of the presence of an x --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
  &lt;/doc&gt;
  </code></pre>
  
  <p>I want to look through each table and replace all cell values ‘a’ with ‘B’. However, if there’s an ‘x’ somewhere in the table, I want to just copy the table unmodified. I know that in this case, I could just do a <code>tr/td[.='x']</code> test on the table to discover this condition. In the real case, though, it’s not so easy to test ahead of time for the condition.</p>
  
  <p>Here’s some XSLT that doesn’t account for the exception:</p>
  
  <pre><code>&lt;xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  
      &lt;xsl:template match="table"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:copy-of select="@*"/&gt;
              &lt;xsl:apply-templates mode="inner"/&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template mode="inner" match="td"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:copy-of select="@*"/&gt;
              &lt;xsl:choose&gt;
                  &lt;xsl:when test=". = 'a'"&gt;
                      &lt;xsl:value-of select="'B'"/&gt;
                  &lt;/xsl:when&gt;
                  &lt;xsl:otherwise&gt;
                      &lt;xsl:value-of select="."/&gt;
                  &lt;/xsl:otherwise&gt;
              &lt;/xsl:choose&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template mode="inner" match="@*|node()" priority="-10"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:apply-templates mode="inner" select="@*|node()"/&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="@*|node()" priority="-10"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:apply-templates select="@*|node()"/&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  &lt;/xsl:stylesheet&gt;
  </code></pre>
  
  <p>The output of that is:</p>
  
  <pre><code>&lt;doc&gt;
      &lt;block&gt;some text, just copy.&lt;/block&gt;
      &lt;!-- the following table should have B substituted for a --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
      &lt;block&gt;some more text, just copy.&lt;/block&gt;
      &lt;!-- the following table should be copied unaltered because of the presence of an x --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
  &lt;/doc&gt;
  </code></pre>
  
  <p>It did the substitutions in the second table, which I don’t want.</p>
  
  <p>My current solution is to do this:</p>
  
  <ol>
  <li>Emit each table into a variable instead of directly into the output</li>
  <li>If the exception occurs, emit an <code>&lt;EXCEPTION/&gt;</code> tag</li>
  <li>After each table is processed, look through the variable for the <code>&lt;EXCEPTION/&gt;</code> tag.</li>
  <li>If the exception happened, copy the original table, else copy the contents of the variable.</li>
  </ol>
  
  <p>Here’s the modified code:</p>
  
  <pre><code>&lt;xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  
      &lt;xsl:template match="table"&gt;
          &lt;xsl:variable name="result"&gt;
              &lt;xsl:copy&gt;
                  &lt;xsl:copy-of select="@*"/&gt;
                  &lt;xsl:apply-templates mode="inner"/&gt;
              &lt;/xsl:copy&gt;
          &lt;/xsl:variable&gt;
          &lt;xsl:choose&gt;
              &lt;xsl:when test="$result//EXCEPTION"&gt;
                  &lt;xsl:copy-of select="."/&gt;
              &lt;/xsl:when&gt;
              &lt;xsl:otherwise&gt;
                  &lt;xsl:copy-of select="$result"/&gt;
              &lt;/xsl:otherwise&gt;
          &lt;/xsl:choose&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template mode="inner" match="td"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:copy-of select="@*"/&gt;
              &lt;xsl:choose&gt;
                  &lt;xsl:when test=". = 'a'"&gt;
                      &lt;xsl:value-of select="'B'"/&gt;
                  &lt;/xsl:when&gt;
                  &lt;xsl:when test=". = 'x'"&gt;
                      &lt;EXCEPTION/&gt;
                  &lt;/xsl:when&gt;
                  &lt;xsl:otherwise&gt;
                      &lt;xsl:value-of select="."/&gt;
                  &lt;/xsl:otherwise&gt;
              &lt;/xsl:choose&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template mode="inner" match="@*|node()" priority="-10"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:apply-templates mode="inner" select="@*|node()"/&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="@*|node()" priority="-10"&gt;
          &lt;xsl:copy&gt;
              &lt;xsl:apply-templates select="@*|node()"/&gt;
          &lt;/xsl:copy&gt;
      &lt;/xsl:template&gt;
  &lt;/xsl:stylesheet&gt;
  </code></pre>
  
  <p>And the correct output:</p>
  
  <pre><code>&lt;doc&gt;
      &lt;block&gt;some text, just copy.&lt;/block&gt;
      &lt;!-- the following table should have B substituted for a --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
      &lt;block&gt;some more text, just copy.&lt;/block&gt;
      &lt;!-- the following table should be copied unaltered because of the presence of an x --&gt;
      &lt;table&gt;
          &lt;tr&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;
          &lt;tr&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;/tr&gt;
      &lt;/table&gt;
  &lt;/doc&gt;
  </code></pre>