Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
| Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old May 7th, 2006, 08:05 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default XSLT and SELECT with a specfic date range

Hi,

I have to populate a select list box(select - options) with a date range which will come from an xml document like this:

<Products>
  <Product prodID="PRD0099" name="Azasan" class="Transplant/Immunosupp" company="Aai Pharma" firsIns="2003-03-01" lastIns="2004-12-31" />
  <Product prodID="PRD0215" name="Darvocet A500" class="Propoxyphenes" company="Aai Pharma" firsIns="2003-02-01" lastIns="2005-09-30" />
</Products>

I have sorted the xml and got the first date - firsIns="2003-02-01" and last date - lastIns="2005-09-30" and stored in a global variable


What I want to do is from the first date 2003-02-01 and the last date 2005-09-30 return all the years and months in between in this format:


Example: 2003-02-01 to 2005-09-30
Output should be:

<select id="selDummyDate1">
 <option value="2003.2">Feb 03</option>
 <option value="2003.3>Mar 03</option>
 <option value="2003.4>Apr 03</option>
---------up to -----
 <option value="2003.12>Dec 03</option>
---then---
<option value="2004.1>Jan 04</option>
------up to ------
<option value="2004.12>Dec 04</option>
---then----
<option value="200.1>Jan 05</option>
----up to-----
<option value="2005.9>Sep 05</option>
</select>

It should be dynamic so that if the xml change the select range will also change.

I could not figure out a way to do that then I decided to try something else which turned out to be a more difficult approach. I added another node (Months) to the root of the xml and used the xslt below but it still did not work.

I would appreciate very much if someone could have a look at my xslt and help me to achieve the desired output. I really cannot figure out a way to do that. Thanks a lot.

<Products>
  <Product prodID="PRD0099" name="Azasan" class="Transplant/Immunosupp" company="Aai Pharma" firsIns="2003-03-01" lastIns="2004-12-31" />
  <Product prodID="PRD0215" name="Darvocet A500" class="Propoxyphenes" company="Aai Pharma" firsIns="2003-02-01" lastIns="2005-09-30" />
  <Months culture="en-gb">
    <month name="Jan" idx="0"/>
    <month name="Feb" idx="1"/>
    <month name="Mar" idx="2"/>
    <month name="Apr" idx="3"/>
    <month name="May" idx="4"/>
    <month name="Jun" idx="5"/>
    <month name="Jul" idx="6"/>
    <month name="Aug" idx="7"/>
    <month name="Sep" idx="8"/>
    <month name="Oct" idx="9"/>
    <month name="Nov" idx="10"/>
    <month name="Dec" idx="11"/>
  </Months>

==============

XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:d="data">
<xsl:output method="html" indent="yes"/>

  <xsl:variable name ="firstDate">
    <xsl:apply-templates select ="Products" mode ="firstD" />
  </xsl:variable>

  <xsl:variable name ="lastDate">
    <xsl:apply-templates select ="Products" mode ="lastD" />
  </xsl:variable>

  <xsl:variable name="firstYear">
    <xsl:value-of select ="substring($firstDate,3,2)"/>
  </xsl:variable>

  <xsl:variable name="lastYear">
    <xsl:value-of select ="substring($lastDate,3,2)"/>
  </xsl:variable>

  <xsl:variable name="firstMonth">
    <xsl:value-of select ="substring($firstDate,7,1)"/>
  </xsl:variable>


  <xsl:variable name="lastMonth">
    <xsl:value-of select ="substring($lastDate,7,1)"/>
  </xsl:variable>

<xsl:template match="/">
  <select id="selDate">
    <xsl:apply-templates select="//Months/month" mode="header" />
  </select>
</xsl:template>


  <xsl:template match ="Products" mode ="firstD">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@firsIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=1">

        <xsl:value-of select ="@firsIns"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match ="Products" mode ="lastD">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@lastIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=last()">

        <xsl:value-of select ="@lastIns"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>


  <xsl:template match="//Months/month" mode="header">
    <xsl:call-template name="getHdrMonth">
      <xsl:with-param name="idx" select="position() - 1" />
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="getHdrMonth">
    <xsl:param name="idx" />
    <xsl:variable name="mIdx" select="((number($firstMonth) + number($idx) - 1) mod 12)" />
    <xsl:variable name="yr">
      <xsl:choose>
        <xsl:when test="$mIdx &lt; $idx">
          <xsl:call-template name="padLeft">
            <xsl:with-param name="padNum" select="string((number($firstYear) + 1) mod 100)" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$firstYear"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <option>
      <xsl:attribute name ="value">
        <xsl:value-of select ="concat('20',$yr,'.')"/>
      </xsl:attribute>
      <xsl:value-of select="concat(//Months/month[@idx=$mIdx]/@name,' ','20',$yr)"/>
    </option>
  </xsl:template>


  <xsl:template name="padLeft">
    <xsl:param name="padNum" />
    <xsl:choose>
      <xsl:when test="string-length($padNum) = 1">
        <xsl:value-of select="concat('0',$padNum)" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$padNum" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>



 
Old May 7th, 2006, 08:27 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

The basic approach in version 1.0 with dates is to use the translate function to change the date's format from yyyy-mm-dd to yyyymmdd.
You can then sort and compare using numerical comparison.

--

Joe (Microsoft MVP - XML)
 
Old May 7th, 2006, 10:06 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Joe,

Thanks for your quick reply.

Could you please send me some sample code or change my code to show me how to do that. I think I am not good enough in xlst to figure out how to do that.

As I said before what I need to do is just populate a select tag dynamically using a date range like these

 2003-02-01 to 2005-09-30

And the output will be like this:

<select id="selDummyDate1">
 <option value="2003.2">Feb 03</option>
 <option value="2003.3>Mar 03</option>
 <option value="2003.4>Apr 03</option>
---------up to -----
 <option value="2003.12>Dec 03</option>
---then---
<option value="2004.1>Jan 04</option>
------up to ------
<option value="2004.12>Dec 04</option>
---then----
<option value="200.1>Jan 05</option>
----up to-----
<option value="2005.9>Sep 05</option>
</select>

I would be very greatful if youc can spare some time to help.

Cheers



 
Old May 7th, 2006, 10:11 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Joe,

I forgot to mention that I have already managed to sort the xml. If you see my xslt, I have some global variables with the dates I need.

The only problem is how to populate the select from the first date up to the last date including all years and months in between and figure ou a way to add the value of the option in this format <option value="2005.9>Sep 05</option>
.


cheers


 
Old May 7th, 2006, 12:05 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

If you're using XSLT 2.0 this is straightforward because xs:date is supported as a data type, allowing direct comparisons.

In XSLT 1.0 you're probably best off converting the date 1999-12-31 into a number 19991231 (which you can do by calling translate() and then number()) and then doing numeric comparisons.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old May 7th, 2006, 01:18 PM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Michael,

Thanks. I am using XSLT 1.0. Could you please show some sample code for that. I am not sure how to do the numeric comparisons to transform it into a select like this. My ideas was to pass a parameer to the xslt with the initial and last dates and then build the select based on these dates


<select id="selDummyDate1">
 <option value="2003.2">Feb 03</option>
 <option value="2003.3>Mar 03</option>
 <option value="2003.4>Apr 03</option>
---------up to -----
 <option value="2003.12>Dec 03</option>
---then---
<option value="2004.1>Jan 04</option>
------up to ------
<option value="2004.12>Dec 04</option>
---then----
<option value="200.1>Jan 05</option>
----up to-----
<option value="2005.9>Sep 05</option>
</select>

Cheers

 
Old May 8th, 2006, 07:38 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Hi Michael and Joe,

Again many thanks for your help. I read page 560 of XSLT 2nd Edition to learn how to use the translate function and have already used in the revised XSLT below. I have also read pages 87 and 530 which talk about the number() function but I cannot figure out how to do the numeric comparisons to populate the select tag in the required format.

Curretnly my xslt gets the first date and lat date using the translate and number function. Please note that I am using XSLT 1.0:

20030201
20050930

But how can I loop through these dates to fill the select tag like this:

<select id="selDummyDate1">
 <option value="2003.2">Feb 03</option>
 <option value="2003.3>Mar 03</option>
 <option value="2003.4>Apr 03</option>
---------up to -----
 <option value="2003.12>Dec 03</option>
---then---
<option value="2004.1>Jan 04</option>
------up to ------
<option value="2004.12>Dec 04</option>
---then----
<option value="200.1>Jan 05</option>
----up to-----
<option value="2005.9>Sep 05</option>
</select>

Please could you give me some help with the code. Is it too dificult to achieve this with xslt ??

Cheers

CP

================================================== ========
XML

<Products>
  <Product prodID="PRD0099" name="Azasan" class="Transplant/Immunosupp" company="Aai Pharma" firsIns="2003-03-01" lastIns="2004-12-31" />
  <Product prodID="PRD0215" name="Darvocet A500" class="Propoxyphenes" company="Aai Pharma" firsIns="2003-02-01" lastIns="2005-09-30" />
</Products>


================================================== ========
XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:d="data">
<xsl:output method="html" indent="yes"/>


  <xsl:variable name ="firstDateTrans">
    <xsl:apply-templates select ="Products" mode ="tran1" />
  </xsl:variable>

  <xsl:variable name ="lastDateTrans">
    <xsl:apply-templates select ="Products" mode ="tran2" />
  </xsl:variable>

  <xsl:template match="/">

    <xsl:value-of select ="$firstDateTrans"/>
    <br />
    <xsl:value-of select ="$lastDateTrans"/>
    <br />
    <br />

    <select id="selDate">

    </select>
  </xsl:template>

  <xsl:template match ="Products" mode ="tran2">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@lastIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=last()">
        <xsl:value-of select ="number(translate(@lastIns, '-', ''))"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match ="Products" mode ="tran1">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@firsIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=1">
        <xsl:value-of select ="number(translate(@firsIns, '-', ''))"/>
      </xsl:if>
    </xsl:for-each>

  </xsl:template>

</xsl:stylesheet>


 
Old May 8th, 2006, 08:10 AM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I was trying to see where "these dates" were in your source XML, but I think I've now worked out what you want.

The cleanest way is a recursive template, something like this:

<xsl:template name="month-list">
  <xsl:param name="start"/>
  <xsl:param name="end"/>
  <xsl:if test="$start &lt;= $end">
    <xsl:variable name="year" select="substring($start,1,4)"/>
    <xsl:variable name="month"
                  select="number(substring($start, 6, 2)"/>
    <option value="{concat($year, '.', $month)}">
      <xsl:value-of select="substring('JanFebMarApr...',
             ($month - 1)*3, 3)"/>
    </option>
    <xsl:variable name="next-month">
     <xsl:choose>
       <xsl:when test="$month=12">
         <xsl:value-of select="($year+1)*100 + 1"/>
       </xsl:when>
       <xsl:otherwise>
         <xsl:value-of select="$start + 1"/>
       </xsl:otherwise>
     </xsl:choose>
    </xsl:variable>
    <xsl:call-template name="month-list">
      <xsl:with-param name="$start" select="$next-month"/>
      <xsl:with-param name="$end" select="$end"/>
    </
  </
</

Then call it with parameters 200302, 200509, which you can obtain as floor($x div 100) where $x is the value you obtained from translate().


Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old May 8th, 2006, 08:56 AM
Friend of Wrox
Points: 1,243, Level: 13
Points: 1,243, Level: 13 Points: 1,243, Level: 13 Points: 1,243, Level: 13
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2003
Location: , , United Kingdom.
Posts: 290
Thanks: 24
Thanked 0 Times in 0 Posts
Default

Michael,

Many thanks.

I tried to use the template you provided but there were some typos:

The if statement was not closed so I closed it at the bottom of the template.
"<xsl:if test="$start &lt;= $end">"

Where it read

<xsl:call-template name="month-list">
      <xsl:with-param name="$start" select="$next-month"/>
      <xsl:with-param name="$end" select="$end"/>

The MSXML 6 parser gave an error saying that the name attribute was invalid. The value $start and $end are invalid according to their datatype. The $ character hexadecimal value 0x24 cannot be included in a name.

So I changed it to:

<xsl:call-template name="month-list">
        <xsl:with-param name="start" select="$next-month"/>
        <xsl:with-param name="end" select="$end"/>
      </xsl:call-template>

However, when I tried to run it, it took a long time to finish and I got the following error:

OutOFMemoryException transforming XML.

OutOfMemoryException
--------------------
Exception of type 'System.OutOfMemoryException' was thrown.

Do you have any ideas of what is causing this?

This is the new xslt using your template.

Cheers Michael

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:d="data">
<xsl:output method="html" indent="yes"/>


  <xsl:variable name ="firstDateTrans">
    <xsl:apply-templates select ="Products" mode ="tran1" />
  </xsl:variable>

  <xsl:variable name ="lastDateTrans">
    <xsl:apply-templates select ="Products" mode ="tran2" />
  </xsl:variable>

  <xsl:template match="/">

    <select id="selDate">
      <xsl:call-template name="month-list">
        <xsl:with-param name="start" select="$firstDateTrans"/>
        <xsl:with-param name="end" select="$lastDateTrans"/>
      </xsl:call-template>
    </select>
  </xsl:template>

  <xsl:template name="month-list">
    <xsl:param name="start"/>
    <xsl:param name="end"/>
    <xsl:if test="$start &lt;= $end">
      <xsl:variable name="year" select="substring($start,1,4)"/>
      <xsl:variable name="month" select="number(substring($start, 6, 2))"/>
      <option value="{concat($year, '.', $month)}">
        <xsl:value-of select="substring('JanFebMarApr...',($month - 1)*3, 3)"/>
      </option>
      <xsl:variable name="next-month">
        <xsl:choose>
          <xsl:when test="$month=12">
            <xsl:value-of select="($year+1)*100 + 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="$start + 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:call-template name="month-list">
        <xsl:with-param name="start" select="$next-month"/>
        <xsl:with-param name="end" select="$end"/>
      </xsl:call-template>
     </xsl:if>
  </xsl:template>




  <xsl:template match ="Products" mode ="tran2">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@lastIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=last()">
        <xsl:value-of select ="number(translate(@lastIns, '-', ''))"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match ="Products" mode ="tran1">
    <xsl:for-each select ="Product">
      <xsl:sort select ="@firsIns" order="ascending" data-type="text"/>
      <xsl:if test ="position()=1">
        <xsl:value-of select ="number(translate(@firsIns, '-', ''))"/>
      </xsl:if>
    </xsl:for-each>

  </xsl:template>



</xsl:stylesheet>


==========================================
XML

<Products>
  <Product prodID="PRD0099" name="Azasan" class="Transplant/Immunosupp" company="Aai Pharma" firsIns="2003-03-01" lastIns="2004-12-31" />
  <Product prodID="PRD0215" name="Darvocet A500" class="Propoxyphenes" company="Aai Pharma" firsIns="2003-02-01" lastIns="2005-09-30" />
</Products>




 
Old May 8th, 2006, 09:28 AM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

What environment are you using for debugging this?

If you run it using Saxon from the command line, you get some pretty good clues:

<select xmlns:d="data" id="selDate">
   <option value="2.00.2">nFe</option>
   <option value="2.00.2">nFe</option>
   <option value="2.00.2">nFe</option>
   <option value="2.00.2">nFe</option>
   <option value="2.00.2">nFe</option>
...
   <option value="2.00.2">nFe</option>
   <option value="2.00.2">nFe</option>
   <option value="2.00.3">bMa</option>
   <option value="2.00.3">bMa</option>
   <option value="2.00.3">bMa</option>
   <option value="2.00.3">bMa</option>
   <option value="2.00.3">bMa</option>
   <option value="2.00.3">bMa</option>

So I think your initial call is setting $year to "2.00". But I'll leave the debugging to you. I'm afraid I don't find XSLT 1.0 programming much fun - under 2.0, the chances are that this error would be immediately revealed by virtue of the stronger type checking. (And you wouldn't have to use recursion anyway, you could use date-time arithmetic directly).

Sorry about the errors by the way, but I don't normally try to write code for people, I just try to give them a kick-start so they can do it themselves.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference




Similar Threads
Thread Thread Starter Forum Replies Last Post
Select range node Christ M XSLT 4 January 25th, 2008 12:47 PM
How to find a date range between another date rang tayvonne Access 2 August 3rd, 2006 09:50 AM
Need Help With Date Range rpainter Crystal Reports 0 June 20th, 2005 01:01 PM
Help with Date Range PacMed Access 1 February 1st, 2005 12:30 PM
Date Range mrideout BOOK: Beginning ASP.NET 1.0 0 January 2nd, 2005 07:18 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.