Chapter 17

Debugging XSLT Transformations

In This Chapter

bullet Testing for specific conditions at run-time

bullet Adding a conditional debug mode

bullet Tracing your XML transformation

I have a confession to make: Yes, it may be hard to believe, but I have had bugs appear in my XSLT stylesheets before. Perhaps you have never had that happen before, and if so, then feel free to skip this chapter. But, if you’re a mere mortal like me, read on. Knowing how to debug in XSLT is an important part of the stylesheet authoring process.

Debugging is one of those necessary evils of programming. It’s not much fun, but it sure is necessary. Unfortunately, XSLT is not a language that lends itself to easy debugging. Besides the xsl:message element, there is little in the language that provides debugging support. Most of the time, debugging becomes a trial and error process consisting of modifying the stylesheet and then running the transformation. If the results aren’t what you expect, then modify the XSLT and try again and again until everything works.

However, as you find out in this chapter, some XSLT debugging techniques are valuable tools that can help you track down problems.

TechnicalStuff

Got that deer in the headlights look? You may have if you’ve programmed in a traditional language and are trying to figure out how to debug XSLT. Debugging in a language like C, Java, or Visual Basic is relatively straightforward because you can intuitively see a program being executed line by line as you check the code. But exactly how XSLT is processed is far more opaque, often resembling the “man behind the curtain.” But, as you begin to think in XSLT, this new process becomes second nature.

Conditionally Halting Execution

A debugging technique that can be worthwhile is to halt execution of your transformation when certain conditions occur at the time of processing. If those conditions occur, the processor can flag you with an xsl:message instruction and you can optionally decide whether or not to terminate processing.

Consider the coffee.xml source code in Listing 17-1.

Listing 17-1: coffee.xml

<?xml version=”1.0”?>

<coffees>

 <region name=”Latin America”>

  <coffee name=”Guatemalan Express” origin=”Guatemala”>

    <taste>Mild and Bland</taste>

    <price>11.99</price>

    <availability>Year-round</availability>

    <bestwith>Breakfast</bestwith>

  </coffee>

  <coffee name=”Costa Rican Deacon” origin=”Costa Rica”>

    <taste>Exotic and Untamed</taste>

    <price>12.99</price>

    <availability>Year-round</availability>

    <bestwith>Dessert</bestwith>

  </coffee>

 </region>

 <region name=”Africa”>

  <coffee name=”Ethiopian Sunset Supremo” origin=”Ethiopia”>

    <taste>Exotic and Untamed</taste>

    <price>14.99</price>

    <availability>Limited</availability>

    <bestwith>Chocolate</bestwith>

  </coffee>

  <coffee name=”Kenyan Elephantismo” origin=”Kenya”>

    <taste>Solid yet Understated</taste>

    <price>3.99</price>

    <availability>Year-round</availability>

    <bestwith>Elephant Ears</bestwith>

  </coffee>

  </region>

</coffees>

When I transform this source document, I want to check the price of each coffee element to be sure that the price is valid. If the price is below $6, then I want to stop the transformation, because a data error must have occurred. To set this up, I create a template rule for the coffee element:

  <!-- Check price of coffee before continuing -->

  <xsl:template match=”coffee”>

    <xsl:if test=”price &lt; 6”>

      <xsl:message terminate=”yes”>

       ERROR: Hold on there Tex. Price should never be under $6.00. Price for <xsl:value-of select=”@name”/> is <xsl:value-of select=”price”/>. 

      </xsl:message>

    </xsl:if>

    <xsl:apply-templates select=”price”/>

  </xsl:template>

Inside this template rule, I use an xsl:if instruction to test the price of coffee. If it is less than $6, then xsl:message gives the error text and stops the execution of the process.

The entire XSLT stylesheet is shown here:

      <xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”>

  <!-- Check price of coffee before continuing -->

  <xsl:template match=”coffee”>

    <xsl:if test=”price &lt; 6”>

      <xsl:message terminate=”yes”>

       ERROR: Hold on there Tex. Price should never be under $6.00. Price for <xsl:value-of select=”@name”/> is <xsl:value-of select=”price”/>. 

      </xsl:message>

    </xsl:if>

    <xsl:apply-templates select=”price”/>

  </xsl:template>

  <!-- Add new element -->

  <xsl:template match=”price”>

    Today’s price for <xsl:value-of select=”../@name”/> is <xsl:value-of select=”.”/>

  </xsl:template>

</xsl:stylesheet> 

When this stylesheet is applied to the coffee.xml file (refer to Listing 17-1), the Kenyan Elephantismo coffee causes the xsl:if instruction to trigger, so the XSLT processor halts and reports the following error:

      ERROR: Hold on there Tex. Price should never be under $6.00. Price for Kenyan Elephantismo is 3.99.

Adding a Conditional Debug Mode

Many programming languages have a way to run a program in debug mode, which is typically used as a way to obtain additional information about  run-time conditions (conditions at the time the transformation occurs). You can add this kind of debug code to your stylesheet, but before you can use it for production, you have to either remove the debug instructions or else comment them out, both of which are inefficient and may lead to the introduction of other, unexpected bugs.

However, you can add a debug mode to your stylesheets and have the debug code conditionally execute without modifying the stylesheet itself. An xsl:param element is the element of choice for this task, because XSLT allows you to specify its value outside the stylesheet at the time you’re performing the transformation.

To do so, set up your stylesheet by defining a parameter named debug that is given the value of off :

  <xsl:param name=”debug”>off</xsl:param>

xsl:choose is a good instruction to add to your stylesheet for testing the value of the debug parameter. If it has the value of ‘on’, then perform the debug instructions in xsl:when, if not, then execute the regular xsl: otherwise instructions:

    <xsl:choose>

      <xsl:when test=”$debug = ‘on’”>

        <!-- If debug is on, do this -->

      </xsl:when>

      <xsl:otherwise>

        <!-- Otherwise, do this -->

      </xsl:otherwise>

    </xsl:choose>

Consider the following use of a conditional debug mode. When debug mode is on, I output a lot of extra debugging information to the result document. Specifically, I add a debugdata element prior to the rest of the result. I stuff this element with debugging information. I also nest the whole result document in a debugmode element and nest the normal results inside a document element. Further, I add a conditional test to ensure that the processedby parameter is specified. The stylesheet is provided here:

      <xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”>

  <xsl:param name=”debug”>off</xsl:param>

  <xsl:param name=”processedby”>none</xsl:param>

  <!-- Test for Debug mode -->

  <xsl:template match=”/”>

    <!-- Force a processedby param to be specified -->

    <xsl:if test=”$processedby = ‘none’”>

      <xsl:message terminate=”yes”>No processedby parameter specified. Unable to continue.</xsl:message>

    </xsl:if>

      

    <xsl:choose>

      <!-- If debug is on, then add extra debug info -->

      <xsl:when test=”$debug = ‘on’”>

        <debugmode>

          <xsl:comment>*** Debug Mode - For Testing Purposes Only ***</xsl:comment>

          <debugdata>

            <processedby><xsl:value-of select=”$processedby”/></processedby>

            <namespace-uri><xsl:value-of select=”namespace-uri()”/></namespace-uri>

            <regioncount><xsl:value-of select=”count(//region)”/></regioncount>

            <coffeecount><xsl:value-of select=”count(//coffee)”/></coffeecount>

          </debugdata>

          <document>

          <xsl:apply-templates/>

          </document>

        </debugmode>

      </xsl:when>

      <!-- If debug is off, process as normal -->

      <xsl:otherwise>

        <xsl:apply-templates/>

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>

  <!-- Copy everything else over -->

  <xsl:template match=”@*|node()”>

    <xsl:copy>

      <xsl:apply-templates select=”@*|node()”/>

    </xsl:copy>

  </xsl:template>

  <!-- Add new element -->

  <xsl:template match=”coffee”>

    <coffee>

      <xsl:apply-templates/>

      <discountprice><xsl:value-of select=”format-number( price*.8, ‘##.##’ )”/></discountprice>

    </coffee>

  </xsl:template>

  

</xsl:stylesheet>

When I specify the debug parameter to be on at process time (see Chapter 8 to find out about how to do this), I get the following result:

<?xml version=”1.0” encoding=”utf-8”?>

<debugmode>

  <!--*** Debug Mode - For Testing Purposes Only ***-->

  <debugdata>

    <processedby>$processedby</processedby>

    <namespace-uri/>

    <regioncount>2</regioncount>

    <coffeecount>4</coffeecount>

  </debugdata>

  <document>

<coffees>

 <region name=”Latin America”>

  <coffee>

    <taste>Mild and Bland</taste>

    <price>11.99</price>

    <availability>Year-round</availability>

    <bestwith>Breakfast</bestwith>

  <discountprice>9.59</discountprice></coffee>

  <coffee>

    <taste>Exotic and Untamed</taste>

    <price>12.99</price>

    <availability>Year-round</availability>

    <bestwith>Dessert</bestwith>

  <discountprice>10.39</discountprice></coffee>

 </region>

 <region name=”Africa”>

  <coffee>

    <taste>Exotic and Untamed</taste>

    <price>14.99</price>

    <availability>Limited</availability>

    <bestwith>Chocolate</bestwith>

  <discountprice>11.99</discountprice></coffee>

  <coffee>

    <taste>Solid yet Understated</taste>

    <price>3.99</price>

    <availability>Year-round</availability>

    <bestwith>Elephant Ears</bestwith>

  <discountprice>3.19</discountprice></coffee>

  </region>

</coffees>

          </document>

</debugmode>

Tracing through Your Code

Outside a full-scale debugger, the best way to obtain detailed information about how the XSLT processor is transforming your XML document is to trace through each of the transformation steps. Tracing is a debugging technique that gives you a step-by-step account of the transformation taking place. XSLT itself has no support for stepping through your stylesheet like this, but many XSLT processors add tracing as a feature. Take, for example, the SAXON processor. It has a specific option that you can turn on to output tracing information. So, suppose I apply the following stylesheet to the coffee.xml document shown in Listing 17-1:

 <xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”>

  <xsl:template match=”coffees”>

    <coffees>

      <xsl:apply-templates/>

    </coffees>

  </xsl:template>

  <!-- Add new element -->

  <xsl:template match=”coffee”>

    <coffee>

      <xsl:apply-templates/>

      <discountprice><xsl:value-of select=”format-number( price*.8, ‘##.##’ )”/></discountprice>

    </coffee>

  </xsl:template>

</xsl:stylesheet>

When I run the SAXON processor from a command line, I can turn trace support on by specifying the -T option:

saxon -T -o c:output.txt coffee.xml coffee_tracer.xsl

In addition to generating the normal result document, the processor also spits out the following tracing information to the command line. Notice that the information is captured in an XML structure, so you can analyze it by using your XML and XSLT tools if you want:

<trace>

<Top-level element=”xsl:template” line=”3” file=”file:/C:/Xslt/coffee_tracer.xsl” precedence=”0”/>

<Top-level element=”xsl:template” line=”10” file=”file:/C:/Xslt/coffee_tracer.xsl” precedence=”0”/>

<Source node=”/coffees[1]” line=”2” mode=”*default*”>

 <Instruction element=”xsl:template” line=”3”>

  <Instruction element=”coffees” line=”4”>

   <Instruction element=”xsl:apply-templates” line=”5”>

    <Source node=”/coffees[1]/region[1]/coffee[1]” line=”4” mode=”*default*”>

     <Instruction element=”xsl:template” line=”10”>

      <Instruction element=”coffee” line=”11”>

       <Instruction element=”xsl:apply-templates” line=”12”>

       </Instruction> <!-- xsl:apply-templates -->

       <Instruction element=”discountprice” line=”13”>

        <Instruction element=”xsl:value-of” line=”13”>

        </Instruction> <!-- xsl:value-of -->

       </Instruction> <!-- discountprice -->

      </Instruction> <!-- coffee -->

     </Instruction> <!-- xsl:template -->

    </Source><!-- /coffees[1]/region[1]/coffee[1] -->

    <Source node=”/coffees[1]/region[1]/coffee[2]” line=”10” mode=”*default*”>

     <Instruction element=”xsl:template” line=”10”>

      <Instruction element=”coffee” line=”11”>

       <Instruction element=”xsl:apply-templates” line=”12”>

       </Instruction> <!-- xsl:apply-templates -->

       <Instruction element=”discountprice” line=”13”>

        <Instruction element=”xsl:value-of” line=”13”>

        </Instruction> <!-- xsl:value-of -->

       </Instruction> <!-- discountprice -->

      </Instruction> <!-- coffee -->

     </Instruction> <!-- xsl:template -->

    </Source><!-- /coffees[1]/region[1]/coffee[2] -->

    <Source node=”/coffees[1]/region[2]/coffee[1]” line=”18” mode=”*default*”>

     <Instruction element=”xsl:template” line=”10”>

      <Instruction element=”coffee” line=”11”>

       <Instruction element=”xsl:apply-templates” line=”12”>

       </Instruction> <!-- xsl:apply-templates -->

       <Instruction element=”discountprice” line=”13”>

        <Instruction element=”xsl:value-of” line=”13”>

        </Instruction> <!-- xsl:value-of -->

       </Instruction> <!-- discountprice -->

      </Instruction> <!-- coffee -->

     </Instruction> <!-- xsl:template -->

    </Source><!-- /coffees[1]/region[2]/coffee[1] -->

    <Source node=”/coffees[1]/region[2]/coffee[2]” line=”24” mode=”*default*”>

     <Instruction element=”xsl:template” line=”10”>

      <Instruction element=”coffee” line=”11”>

       <Instruction element=”xsl:apply-templates” line=”12”>

       </Instruction> <!-- xsl:apply-templates -->

       <Instruction element=”discountprice” line=”13”>

        <Instruction element=”xsl:value-of” line=”13”>

        </Instruction> <!-- xsl:value-of -->

       </Instruction> <!-- discountprice -->

      </Instruction> <!-- coffee -->

     </Instruction> <!-- xsl:template -->

    </Source><!-- /coffees[1]/region[2]/coffee[2] -->

   </Instruction> <!-- xsl:apply-templates -->

  </Instruction> <!-- coffees -->

 </Instruction> <!-- xsl:template -->

</Source><!-- /coffees[1] -->

</trace>

More Powerful Debugging

If you are having a particular problem in which nothing else you have tried works, then you may want to consider getting a full-scale XSLT debugger. You can find debuggers with:

bullet Full visual step-through capabilities to allow you to see and perform a transformation one step at a time.

bullet Breakpoint support to stop the transformation at a specific point so you can check its status.

bullet Parameter and variable “watches” to find out their current value during the processing of a document.

Two of particular note are commercial software tools, though they have trial versions that you can download:

For more tool information, one good Web site to check out is www.xslt.com/xslt_tools_editors.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.93.221