Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
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 October 13th, 2008, 01:23 PM
Authorized User
 
Join Date: Oct 2008
Posts: 26
Thanks: 2
Thanked 0 Times in 0 Posts
Default sort and display first item only

I'm attempting to take an XML file, sort by date descending, then display only the first item. Since there are often many dates after $today, I only want the most recent, not all of them. I know it has to be something I'm doing with the position() thing, but I can't figure out what. Can anyone help? Thanks.

Code:
<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xdt="http://www.w3.org/2005/02/xpath-datatypes">
    <xsl:param name="project"/>
    <xsl:param name="today"/>

    <xsl:template match="calendar" name="main">
        <xsl:if test="//event/program[@id=$project]">
        <xsl:element name="h1">
            <xsl:attribute name="class">side</xsl:attribute>    
           UPCOMING MEETING
           </xsl:element>
        <xsl:apply-templates>
            <xsl:sort select="event/evdate" order="ascending" data-type="number"/>    
        </xsl:apply-templates>
        <xsl:element name="div">
            <xsl:attribute name="class">hrbrk</xsl:attribute>
        </xsl:element>
        </xsl:if>
    </xsl:template>

    <xsl:template match="event">
        <xsl:if test="program[@id=$project] and type='meeting'">
            <xsl:if test="evdate &gt;= $today and position() = 1">            <xsl:element name="ul">
                    <xsl:attribute name="class">sbox</xsl:attribute>
                    <xsl:element name="li">
                        <xsl:element name="b">
                            <xsl:attribute name="style">color: #a77909</xsl:attribute>
                            <xsl:call-template name="format-date"/> :: <xsl:value-of select="evstart"/>-<xsl:value-of select="evend"/>
                        </xsl:element>#160;<xsl:value-of select="evname"/> at <xsl:value-of select="location"/>
                    </xsl:element>
                </xsl:element>
            </xsl:if>
        </xsl:if>
    </xsl:template>
    <xsl:template name="format-date">
        <xsl:variable name="d">
            <xsl:value-of select="evdate"/>
        </xsl:variable>
        <xsl:call-template name="long_date">
            <xsl:with-param name="date" select="$d"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="long_date">
        <xsl:param name="date"/>
        <xsl:value-of select="substring($date, 7, 2)"/>#160;
    <xsl:variable name="month" select="substring($date, 5, 2)"/>
        <xsl:choose>
            <xsl:when test="$month=01">January</xsl:when>
            <xsl:when test="$month=02">February</xsl:when>
            <xsl:when test="$month=03">March</xsl:when>
            <xsl:when test="$month=04">April</xsl:when>
            <xsl:when test="$month=05">May</xsl:when>
            <xsl:when test="$month=06">June</xsl:when>
            <xsl:when test="$month=07">July</xsl:when>
            <xsl:when test="$month=08">August</xsl:when>
            <xsl:when test="$month=09">September</xsl:when>
            <xsl:when test="$month=10">October</xsl:when>
            <xsl:when test="$month=11">November</xsl:when>
            <xsl:when test="$month=12">December</xsl:when>
            <xsl:otherwise>INVALID MONTH</xsl:otherwise>
        </xsl:choose>#160;
      <xsl:value-of select="substring($date, 1, 4)"/>
    </xsl:template>
</xsl:stylesheet>
 
Old October 13th, 2008, 02:13 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Difficult to tell because we can't see your source document.

But it could be a whitespace issue. Use <xsl:strip-space elements=*"/> or <xsl:apply-templates select="*"/> to make sure that the numbering of nodes (ie. the result of position()) is not including whitespace nodes in the count. These will otherwise sort first because the sort key will be NaN.

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 13th, 2008, 02:14 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Oh, by the way, why do you write this long-winded code:

        <xsl:element name="div">
            <xsl:attribute name="class">hrbrk</xsl:attribute>
        </xsl:element>

when you could just write

        <div class="hrbrk"/>

Are you paid by the line, or something?

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 13th, 2008, 02:45 PM
Authorized User
 
Join Date: Oct 2008
Posts: 26
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Thanks. I tried both the
<xsl:strip-space elements="*"/> and <xsl:apply-templates select="*"/>, but neither worked.

Here's the sample xml:
Code:
<calendar>
<event>
    <type>meeting</type>
    <evname>Meeting 2</evname>
    <evdate>20081016</evdate>
    <evstart>12:00pm</evstart>
    <evend>2:00pm</evend>
    <location>Government Center</location>
    <locmap></locmap>
    <program id="growth">Growth Organization</program>
    <contact>Staff Member</contact>
    <email>staff#64;addy.gov</email>
    <siteurl></siteurl>
    <postdate></postdate>        
</event>
<event>
    <type>meeting</type>
    <evname>Meeting 1</evname>
    <evdate>20081010</evdate>
    <evstart>12:00pm</evstart>
    <evend>2:00pm</evend>
    <location>Government Center</location>
    <locmap></locmap>
    <program id="growth">Growth Organization</program>
    <contact>Staff Member</contact>
    <email>staff#64;addy.gov</email>
    <siteurl></siteurl>
    <postdate></postdate>        
</event>
<event>
    <type>meeting</type>
    <evname>Meeting 3</evname>
    <evdate>20081207</evdate>
    <evstart>5:00pm</evstart>
    <evend>7:00pm</evend>
    <location>Government Center</location>
    <locmap></locmap>
    <program id="growth">Growth Organization</program>
    <contact>Staff Member</contact>
    <email>staff#64;addy.gov</email>
    <siteurl></siteurl>
    <postdate></postdate>        
</event>
</calendar>
As far as the long-winded code, goes, I just thought it was bad form to put regular htm tags in the xsl. This is my first time writing xsl (and javascript, for that matter). The boss didn't want to pay for classes, so I'm trying to work all this out on my own. I appreciate all your help.

 
Old October 13th, 2008, 03:09 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

OK, I tried running it.

First thing I got was

Error on line 48 of file:/c:/temp/test.xsl:
  XPTY0004: Cannot compare xs:string to xs:integer

and more of the same for the similar lines like

<xsl:when test="$month=01">January</xsl:when>

That's because you've said version="2.0" in your stylesheet, so when it's run with a 2.0 processor you get the benefit of the stronger type-checking in XSLT 2.0.

I fixed that with quotes around the month, viz:

<xsl:when test="$month='01'">January</xsl:when>

However, if this stylesheet is designed for XSLT 2.0 then you could use the format-date() function for this logic (and if it isn't, then you should change the version attribute to version="1.0").

When I fixed this I got no output at all. Unfortunately that's a real hazard with XSLT - in 2.0 you can declare the types of your variables and parameters which makes it much less likely. In this case it happened because I hadn't spotted the need to supply values for the parameters $project and $today. (I added the attribute required="yes", another 2.0 feature, to stop this happening again).

Then I ran with project=growth today=20081010

which produced output containing only the header and footer.

So I changed the apply-templates call to

        <xsl:apply-templates select="*">
            <xsl:sort select="event/evdate" order="ascending" data-type="number"/>
        </xsl:apply-templates>

as in my suggestion to you, and I got this output:

<?xml version="1.0" encoding="UTF-8"?><h1 class="side">
           UPCOMING MEETING
           </h1><ul class="sbox"><li><b style="color: #a77909">16#160;
    October#160;
      2008 :: 12:00pm-2:00pm</b>#160;Meeting 2 at Government Center</li></ul><div class="hrbrk"/>

Which doesn't look very impressive but it's actually what you asked for. If this isn't the output you wanted, then you need to explain what you wanted.

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 13th, 2008, 04:02 PM
Authorized User
 
Join Date: Oct 2008
Posts: 26
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Hmmm. That didn't work for me either. What I'm looking to do (and perhaps the problem is that I'm going about it the wrong way) is to have a calendar of several meeting dates, some before 'today' (i.e. date variable) and some after. I want to only display the very next date, which could be equal to or greater than today. My problem is that my script is displaying ALL of the future dates rather than only the next item.

(The other minor issue is that there will be several different projects which may have meeting dates on the same day or sooner than another project, so both project id and the very next date iteration must be selected.)

I went ahead and altered the stylesheet back to 1.0 since I'm not using anything like Saxon. Unfortunately, I'm boxed into using strictly client-side stuff since I have no say on our hosting or anything else. Long story, I'm just a whizz on at the bottom of the food chain, doing the best I can...I do appreciate all your help, though.

 
Old October 13th, 2008, 04:23 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

>That didn't work for me either.

Then you need to say exactly what you did (XML input, XSLT input, and operating environment) and exactly what output you got, so that we can see if we can reproduce it.

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 13th, 2008, 04:43 PM
Authorized User
 
Join Date: Oct 2008
Posts: 26
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Sorry, Michael.

What I did was use the two files listed above (xml & xsl) and what resulted was writing the "UPCOMING MEETING HEADER" with the "hrbrk" footer and no content between. A blank.

Earlier attempts, which didn't contain the "and position() = 1" resulted in all dates after the $today variable being printed.
Ditto with:
Code:
<xsl:apply-templates select="*">
            <xsl:sort select="event/evdate" order="ascending" data-type="number"/>    
</xsl:apply-templates>
So, given the xml I gave you, I'm looking for the output of:

UPCOMING MEETING
16 October 2008 :: 12:00pm-2:00pm :: Meeting 2 at Government Center
<div class="hrbrk" />

instead of:

UPCOMING MEETING
16 October 2008 :: 12:00pm-2:00pm :: Meeting 2 at Government Center

7 December 2008 :: 5:00pm-7:00pm :: Meeting 3 at Government Center
<div class="hrbrk" />

 
Old October 13th, 2008, 05:03 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

To investigate further, I need to know how you are running this, because these results are different from mine.

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 14th, 2008, 08:43 AM
Authorized User
 
Join Date: Oct 2008
Posts: 26
Thanks: 2
Thanked 0 Times in 0 Posts
Default

Quote:
quote:Originally posted by mhkay
 To investigate further, I need to know how you are running this, because these results are different from mine.

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
This portion is being written for everything but IE, if that matters. It is hosted on a Linux system, but the clients are probably usually Windows. Client-side javascript affects the transformation (no PHP, no Saxon, nada). I'm using a version of Johann Burkard's script modified to pass parameters to the xsl.

Basically, the javascript goes in, checks to see if the target div exists in the template, then it populates by using the Burkhard script, which I've altered to pass parameters (date & project id) from the template to the xsl. The xsl is supposed to identify all meeting elements in the calendar, identify those meetings belonging to that particular project, sort by date, then display the first meeting date that is equal to or greater than today's date for that project.

The only thing it is doing wrong is that it is selecting ALL dates for that project greater than or equal to today's date, rather than the first instance.

I hope that gives you the info you need.






Similar Threads
Thread Thread Starter Forum Replies Last Post
Sort by date...select....display first item prob athos XSLT 2 November 3rd, 2008 04:31 PM
Display data as per combobox item selected yogeshyl Excel VBA 0 July 28th, 2007 06:17 AM
display datagrid item shwetarani2002 ASP.NET 1.0 and 1.1 Basics 1 July 16th, 2007 07:47 AM
Tab item display prob / hide TABS query socoolbrewster CSS Cascading Style Sheets 4 June 13th, 2006 12:59 PM
Datagrid - selected item display advice janetb ASP.NET 1.0 and 1.1 Basics 2 May 9th, 2006 02:56 PM





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