[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

RE: : XSL processes XML incorrectly when uneven number

Subject: RE: : XSL processes XML incorrectly when uneven number of values returned in array elements
From: "Cave, Neil" <Neil.Cave@xxxxxxxxxxxxxx>
Date: Mon, 13 Mar 2006 11:39:11 +0100
neil row
Hi Charles

Your solution works perfectly using saxon8.7.
Thanks very much for your assistance and good luck with the new job
search

-----Original Message-----
From: cknell@xxxxxxxxxx [mailto:cknell@xxxxxxxxxx]
Sent: 10 March 2006 04:24 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: RE: : XSL processes XML incorrectly when uneven number of
values returned in array elements

I think I've solved your problem. I used Saxon 8.7J to process the XSLT.

I cut down your XML so that I could work only on the part that was
presenting the challenge. I retained the orginal root element, but
excised all other elements but <free-xml> and its children. When you
adapt my solution to the original document you will want to pay
attention to the XPath expressions to insure that you point to the
correct elements.

The entire stylesheet is posted at the end of this message, but I will
highlight sections of it here for exposition.

The first problem we had to solve was how to handle the irregular and
unknown number of rows to be produced. Since all variables in XSLT are
static, the correct approach was to call a named template recursively.

First I calculate the maximum number of rows by creating a variable that
contains an XML fragment holding the set of <optionListx> elements
sorted in descending order by the count of the number of <option>
children.:

  <xsl:template match="free-xml">

    <xsl:variable name="list-with-max-option-children">
      <xsl:for-each select="*">
        <xsl:sort select="count(option)" data-type="number"
order="descending" />
        <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:variable>
    ... The balance of the template goes here ...
  </xsl:template >

Then, by taking the count of the <option> children in the first
<optionListx> element in the sorted fragment, I know the maximum number
of rows to be produced and we begin the recursive calls to the template
that will produce the rows of your table.

  <xsl:template match="free-xml">

    <xsl:variable name="list-with-max-option-children">
      <xsl:for-each select="*">
        <xsl:sort select="count(option)" data-type="number"
order="descending" />
        <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:variable>

    <table>
      <xsl:call-template name="table-row">
        <xsl:with-param name="max-rows"
select="count($list-with-max-option-children/*[1]/option)" />
        <xsl:with-param name="current-row" select="1" />
      </xsl:call-template>
    </table>
  </xsl:template>

Note that we pass two parameters, "max-rows" which sets the stopping
point for the recursive calls to the template named "table-rows", and
"current-row" that selects the position of the set of <option> elements
whose values will be placed in the <td> elements in the row.

Most of the work is done in the template named "table-row":

<xsl:template name="table-row">
  <xsl:param name="max-rows" />
  <xsl:param name="current-row" />

       ... The first thing the template does is check to see if the
maximum number of rows has been reached, it aborts if this is true. ...

  <xsl:if test="$current-row &lt;= $max-rows">
    <tr>
      <xsl:choose>
        <xsl:when
test="/enquiry-data/free-xml/*[1]/option[$current-row]">
          <td><xsl:value-of
select="/enquiry-data/free-xml/*[1]/option[$current-row]" /></td>
        </xsl:when>
        <xsl:otherwise><td /></xsl:otherwise>
      </xsl:choose>

.. Most of the template looks like this. There are known to be exactly
seven columns (which translates to seven <td> elements in each row, so
the bulk of the template consists of <xsl:choose> elements like this,
one for each column.

 The value in the first set of square brackets here,
"*[1]/option[$current-row]" increments for each <xsl:choose> so you get
values from 1 through 7.

The test looks to see if there is an <option> element at that position,
and if so, it places its value in the <td>, otherwise an empty <td /> is
emitted.

After processing all <xsl:choose> statments we make a recursive call to
the same template. ...

 </tr>
      <xsl:call-template name="table-row">
        <xsl:with-param name="max-rows" select="$max-rows" />
        <xsl:with-param name="current-row" select="$current-row + 1" />
      </xsl:call-template>

    ... The trick here is to add one to the value of $current-row and
pass it along to the next call of the template. Although this may look
like we are changing the value of a variable (not permitted in XSLT), we
really aren't. We are creating a new variable with the same name in a
different context.

I've been looking for a new job this week so I've had plenty of time on
my hands to work your problem. Not everyone can spend this kind of time
on a challenge, so the next time you have one, you may or may not get
this much attention. Good luck.

The complete stylesheet:
================
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:strip-space elements="*" />

  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="enquiry-data">
    <xsl:apply-templates />
    </xsl:template>

  <xsl:template match="free-xml">

    <xsl:variable name="list-with-max-option-children">
      <xsl:for-each select="*">
        <xsl:sort select="count(option)" data-type="number"
order="descending" />
        <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:variable>

    <table>
      <xsl:call-template name="table-row">
        <xsl:with-param name="max-rows"
select="count($list-with-max-option-children/*[1]/option)" />
        <xsl:with-param name="current-row" select="1" />
      </xsl:call-template>
    </table>
  </xsl:template>

  <xsl:template name="table-row">
    <xsl:param name="max-rows" />
    <xsl:param name="current-row" />

    <xsl:if test="$current-row &lt;= $max-rows">
      <tr>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[1]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[1]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when test="/free-xml/*[2]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[2]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[3]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[3]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[4]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[4]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[5]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[5]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[6]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[6]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
        <xsl:choose>
          <xsl:when
test="/enquiry-data/free-xml/*[7]/option[$current-row]">
            <td><xsl:value-of
select="/enquiry-data/free-xml/*[7]/option[$current-row]" /></td>
          </xsl:when>
          <xsl:otherwise><td /></xsl:otherwise>
        </xsl:choose>
      </tr>
      <xsl:call-template name="table-row">
        <xsl:with-param name="max-rows" select="$max-rows" />
        <xsl:with-param name="current-row" select="$current-row + 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
  </xsl:stylesheet>

--
Charles Knell
cknell@xxxxxxxxxx - email

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.