Re: [xsl] Need assistance with conditional logic and counter in XSLT 2.0

Subject: Re: [xsl] Need assistance with conditional logic and counter in XSLT 2.0
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Tue, 20 Mar 2012 15:42:00 -0400
Jennifer,

I hope you see this, as it's nearly a week old, but I thought a neater solution was possible to your problem, since you actually need neither counters nor recursion. The XSLT xsl:number instruction will suffice for the numbering.

In fact the only thing that really makes this tricky (other than how to set up xsl:number, which isn't obvious until you learn it) is the fact that you want to drop elements that appear without content.

Another way of handling that problem (not given below) would be to solve this in two passes. That would also give opportunities to make exception handling a bit more graceful (for example, what should happen if no Return Date value is given). But this (single-pass) solution will work also under XSLT 1.0, and solves the problem you presented us with.

  <xsl:template match="Order">
    <div class="order">
      <xsl:apply-templates/>
    </div>
  </xsl:template>

  <xsl:template name="entry">
    <xsl:param name="label"/>
    <xsl:if test="normalize-space()">
      <!-- don't generate output for elements that have no contents
           or only whitespace contents -->
      <p>
        <xsl:call-template name="number"/>
        <xsl:value-of select="$label"/>
        <xsl:text>: </xsl:text>
        <xsl:apply-templates/>
      </p>
    </xsl:if>
  </xsl:template>

  <xsl:template name="number">
    <xsl:number level="multiple" format="1.1. "
      count="OrderNumber[normalize-space()] |
        CustomerID[normalize-space()] | Return[normalize-space()] |
        ReturnReason[normalize-space()] | Total[normalize-space()]"/>
  </xsl:template>

  <xsl:template match="OrderNumber">
    <xsl:call-template name="entry">
      <xsl:with-param name="label">Order number</xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="CustomerID">
    <xsl:call-template name="entry">
      <xsl:with-param name="label">Customer ID</xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="Return">
    <xsl:if test="normalize-space()">
      <p>
        <xsl:call-template name="number"/>
        <xsl:text>Return Date: </xsl:text>
        <xsl:apply-templates select="ReturnDate"/>
      </p>
      <xsl:apply-templates select="ReturnReason"/>
    </xsl:if>
  </xsl:template>

  <xsl:template match="ReturnReason">
    <xsl:call-template name="entry">
      <xsl:with-param name="label">Reason</xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="Total">
    <xsl:call-template name="entry">
      <xsl:with-param name="label">Total</xsl:with-param>
    </xsl:call-template>
  </xsl:template>

Cheers,
Wendell

On 3/14/2012 8:58 AM, Jennifer Elkhouri wrote:
I am very new to XSLT, so thank you in advance for helping me out. My
issue is related to wanting to print output conditionally using
counters. If an element returns no data, I do not want to print the
header for that section (easy enough). However, these headers need to
be numbered ... which is where I am having a problem.

For example, given:

  (XML for record 1234):
  <Order>
   <OrderNumber>1234</OrderNumber>
   <CustomerID>111</CustomerID>
   <Return/>
   <Total>$344</Total>
  </Order>

  (XML for record 1235):
  <Order>
    <OrderNumber>1235</OrderNumber>
    <CustomerID>233</CustomerID>
    <Return>
       <ReturnDate>3/15/2011</ReturnDate>
       <ReturnReason>Too small</ReturnDate>
    </Return>
    <Total>$455</Total>
  </Order>

If someone selected record 1, they expect the transformed html output to be:

  1. Order Number: 1234
  2. Customer ID: 111
  3. Total: $344

  If someone selected record 2, they expect the transformed html output to be:
  1. Order Number: 1235
  2. Customer ID: 233
  3. Return Date: 3/15/2011
  3.1. Return Reason: Too small
  4. Total: $455

I learned quite quickly that I can't do a simple counter. From what
I've read, it is suggested that I use a recursive template. Can you
please help me?

Below is my sample code ... (only showing a snippet for brevity. I am
using XSLT 2.0 and have set a global variable of newLine for a
carriage return.)

<xsl:value-of select="/Order/OrderNumber" /><xsl:text>Order Number:</xsl:text>
<xsl:call-template name="counter">
    <xsl:with-param name="index" select="position()"/>
</xsl:call-template>  <xsl:value-of select="$newLine" />

<xsl:value-of select="/Order/CustomerID" /><xsl:text>Customer ID:</xsl:text>
<xsl:call-template name="counter">
<xsl:with-param name="index" select="position()"/>
</xsl:call-template>  <xsl:value-of select="$newLine" />

<xsl:if test="/Order/Return/ReturnDate">
    <xsl:value-of select="/Order/Return/ReturnDate" /><xsl:text>Return
Date:</xsl:text>
    <xsl:call-template name="counter">
     <xsl:with-param name="index" select="position()"/>
    </xsl:call-template>  <xsl:value-of select="$newLine" />

    <xsl:value-of select="/Order/Return/ReturnReason"
/><xsl:text>Return Reason:</xsl:text>
    <xsl:call-template name="counter">
    <xsl:with-param name="index" select="position()"/>
    </xsl:call-template>  <xsl:value-of select="$newLine" />

</xsl:if>

<xsl:value-of select="/Order/Total" /><xsl:text>Total:</xsl:text>
<xsl:call-template name="counter">
<xsl:with-param name="index" select="position()"/>
</xsl:call-template>  <xsl:value-of select="$newLine" />

<xsl:template name="counter">
   <xsl:param name="index" select="1"/>
   <xsl:if test="$index&lt;= 10">             <!-- I put this in here
so it doesn't fall into an infinite loop. There's got to be a better
way? -->
     <xsl:value-of select="$index"/>
     <xsl:call-template name="counter">
       <xsl:with-param name="index" select="$index + 1"/>
     </xsl:call-template>
   </xsl:if>
</xsl:template>
--
======================================================================
Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.                http://www.mulberrytech.com
17 West Jefferson Street                    Direct Phone: 301/315-9635
Suite 207                                          Phone: 301/315-9631
Rockville, MD  20850                                 Fax: 301/315-8285
----------------------------------------------------------------------
  Mulberry Technologies: A Consultancy Specializing in SGML and XML
======================================================================

Current Thread