tags:

views:

351

answers:

4

Given this XML:

<?xml version="1.0" encoding="utf-8"?>
<content dataType="XML">
  <item type="Promotion" name="Sample Promotion" expires="04/01/2009">
    <![CDATA[
      <p>Details here.</p>
    ]]>
  </item>
  <item type="Promotion" name="Sample Promotion 2" expires="05/01/2009">
    <![CDATA[
      <p>Details here.</p>
    ]]>
  </item>
</content>

What would the XPath be to select the items that have not yet expired?

A: 

XPath doesn't understand date formats, so you'll need to use an extension function to make this work cleanly.

Alternatively, you could use substrings to pull out pieces of the attribute. You'll run into problems if the attributes aren't exactly formatted however, and the expressions will be ginormous.

kdgregory
+1  A: 

I'm new to xslt, but if you're using 1.0 or 1.1, I would get the current date from somewhere and then compare the two strings. This page describes a dos and *nix method for extracting the date. Then I would probably reorganize the values so that they're comparable (I'm assuming that $current_date is a string of the format YYYYMMDD)

<xsl:if test="number(concat(substring(.[@expires],5,4),
   substring(.[@expires],3,2),substring(.[@expires],1,2))&lt;number(
   $current_date">
...
</xsl>

But like I said, I just started with xsl. So, that could be a really clunky solution to an easy problem.

oneporter
+2  A: 

The expired <item> elements can be selected by a single XPath expression, provided the current date is provided -- either in a variable, or as a literal string.

The solution below uses XSLT 1.0 as the hosting language for XPath 1.0.

For convenience, the current date is specified as a global <xsl:param> parameter, named pToday.

Another convenience is that the values for the current year, month and day are defined in the variables $vthisYear, $vthisMonth, and $vthisDay. If necessary, al these variable references can be substituted in the XPath expression with the right-hand-side of their definitions.

The wanted single XPath expression is wrapped-up in the following XSLT transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"
  cdata-section-elements="item"/>


 <xsl:param name="pToday" select="'04/23/2009'"/>

 <xsl:variable name="vthisDay" select=
  "substring($pToday,4,2)"/>

 <xsl:variable name="vthisMonth" select=
  "substring($pToday,1,2)"/>

 <xsl:variable name="vthisYear" select=
  "substring($pToday,7)"/>

    <xsl:template match="/">
     <nonExpired>
       <xsl:copy-of select=
       "/*/item
             [$vthisYear &lt; substring(@expires,7)
            or
               ($vthisYear = substring(@expires,7)
              and
                $vthisMonth &lt; substring(@expires,1,2)
                )
             or
               ($vthisYear = substring(@expires,7)
              and
                $vthisMonth = substring(@expires,1,2)
              and
                $vthisDay &lt;= substring(@expires,4,2)
                )
             ]

       "/>
      </nonExpired>
    </xsl:template>
</xsl:stylesheet>

When the above transformation is applied on the provided XML document:

<content dataType="XML">
    <item type="Promotion" name="Sample Promotion" expires="04/01/2009"><![CDATA[      <p>Details here.</p>    ]]></item>
    <item type="Promotion" name="Sample Promotion 2" expires="05/01/2009"><![CDATA[      <p>Details here.</p>    ]]></item>
</content>

the wanted result is produced:

<nonExpired>
   <item type="Promotion" name="Sample Promotion 2" expires="05/01/2009"><![CDATA[      <p>Details here.</p>    ]]></item>
</nonExpired>

Note: The solution by oneporter will also work, provided some small errors in it are corrected. Also, it needs rework because of its unrealistic expectation that the current date will be provided in a different format than the dates in the @expires attributes.

Dimitre Novatchev