This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: xpath for getting file version


Hi Antony,

> What is the xpath for getting the latest version? I have tried:
>
> <xsl:value-of select="versions/version[number(concat(@year, @month, 
> @day)) > number(concat(../version/@year, ../version/@month,
> ../version/@day))]/@name"/>

That's quite a complex expression, which makes it hard to work out
what's going wrong. Looking at it bit by bit, the expression:

  ../version/@year

returns a node set of all the year attributes of all the versions.
When you use concat() and pass a node set as an argument, the node set
gets converted to a string. You convert a node set to a string by
taking the value of the first node in the node set, so:

  concat(../version/@year, ../version/@month, ../version/@day)

always gives you the date of the *first* version in the list. You go
through all the versions comparing their dates to that version, and
get a node set of all those that have a later date. Then the
xsl:value-of instruction picks the first one of those to give you the
result.

(Note that if you used the same expression in the use attribute of
xsl:key then you'd get the file elements indexed by the names of each
of the later versions, not just the first one.)

If you had the versions actually giving the date in numerical form,
i.e.:

<files>
   <file name="a">
     <versions>
       <version date="20010301" name="test - B"/>
       <version date="20001203" name="test - A"/>
       <version date="20010331" name="test - C"/>
     </versions>
    </file>
</files>

then you could do:

  versions/version[not(@date &lt; ../version/@date)]/@name

but as soon as you need to call any functions to compute the values
that you need to compare.

When it comes down to it, this is a problem of finding a calculated
maximum within an XPath. I think you have at least four options.

First, you could index the files by all their versions and worry about
which is latest later on. So use the key:

<xsl:key name="file-version" match="file" use="versions/version" />

and then when you're processing the files that you retrieve using the
key, filter out those that have been retrieved through an early
version.

Second, you could have a two-stage process (either with two
stylesheets or using a node-set() extension function to do it all in
one stylesheet). In the first pass, construct XML like the above, with
a date attribute giving the entire date, and then use the XPath above
in the key.

Third, you could use an extension function for finding nodes with
the highest values based on an expression, if your processor offers
one. If you use Saxon, you could do:

<xsl:key name="file-version" match="file"
         use="saxon:highest(versions/version,
                            saxon:expression('concat(@year, @month,
                                             @day)'))" />

Finally, you could write your own extension function to find the latest
version of a file. With the EXSLT mechanism, it could look like:

<func:function name="my:latest-version">
  <xsl:for-each select="versions/version">
    <xsl:sort select="concat(@year, @month, @day)"
              data-type="number" order="descending" />
    <xsl:if test="position() = 1">
      <func:result select="@name" />
    </xsl:if>
  </xsl:for-each>
</func:function>

(or you could write a recursive function if speed was an issue)

In a processor that supports EXSLT - Functions (Saxon or 4XSLT), you
could do:

<xsl:key name="file-version" match="file" use="my:latest-version()" />

I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]