Re: [xsl] processing xml question

Subject: Re: [xsl] processing xml question
From: Mike Brown <mike@xxxxxxxx>
Date: Tue, 10 Dec 2002 11:55:48 -0700 (MST)
Falls, Travis D (CASD, IT) wrote:
> if the publish date is after the nowString it isn't
> publishable yet, or the expiration date is before the
> nowString then this survey is expired.
>
> The programming task is to get the most recently
> published item that isn't expired and the previous one
> if there is one.

1. Pass $nowDate into your stylesheet as a top-level parameter
when you invoke the transformation.

2. Get items that have been published but haven't expired.

In SQL this would be something like

       select item
           where pubdate <= $nowString
                 and expdate >= $nowString;

In XPath/XSLT it's

<xsl:variable name="currentItems" 
 select="item[pubdate &lt;= $nowString and expdate &gt;= $nowString]"/>

3. From this set, find the two with the highest pubdates. This is a variation
of the 'how do I find the maximum value' FAQ (tucked away in the section on
sorting).

XPath predicates look at nodes in document order or reverse document order,
depending on the axis, so you can't simply use
$currentItems[position() &lt; 3]. Therefore, you have sort-and-pick 'by hand'.  
This works OK if you don't have too many nodes. Assuming your <item>s are all 
contained in a <data> document element, this stylesheet demonstrates what I mean:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

  <xsl:output method="xml" indent="yes"/>

  <xsl:strip-space elements="*"/>

  <xsl:param name="nowString"/>

  <xsl:template match="/">
    <result>
      <xsl:variable name="currentItems"
       select="data/item[pubdate &lt;= $nowString and expdate &gt;= $nowString]"/>
      <!-- process in sorted order, copying just the ones we need -->
      <xsl:for-each select="$currentItems">
        <xsl:sort select="pubdate" order="descending"/>
        <xsl:if test="position() &lt; 3">
          <xsl:copy-of select="."/>
        </xsl:if>
      </xsl:for-each>
    </result>
  </xsl:template>

</xsl:stylesheet>

There might be other approaches involving keys or recursion.
Someone else will have to tell you what they are.

You didn't say what you want to do with the items once you get them. The above
just copies them to the result tree.

If you need to hang onto them in a variable, you'll have to put them in a
result tree fragment, which you can then convert to a node-set with
exsl:node-set() or your XSLT processor's equivalent extension function for
this purpose. Something like:

<xsl:variable name="recentItemsRTF">
  <xsl:for-each select="$currentItems">
    <xsl:sort select="pubdate" order="descending"/>
    <xsl:if test="position() &lt; 3">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:for-each>
</xsl:variable>
<xsl:variable name="recentItems" select="exsl:node-set($recentItemsRTF)/*"
 xmlns:exsl="http://exslt.org/common"/>

<xsl:for-each select="$recentItems">
  <!-- do something with each here -->
</xsl:for-each>

Check your XSLT processor's docs; for example it may let you create a node-set
instead of a result tree fragment, if you supply a vendor-specific attribute
on the xsl:variable element. And of course, you could skip the creation of 
$recentItems and move the select="exsl:node-set..." down into the for-each.


Mike

-- 
  Mike J. Brown   |  http://skew.org/~mike/resume/
  Denver, CO, USA |  http://skew.org/xml/

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread