Göran Kämpe
Göran Kämpe

Reputation: 63

xslt condition and decimal values

I want to compare two software versions using xsl, for example 1.13 and 1.4 and I want 1.13 to be larger than 1.4.

However, the following xslt condition will fail.

<xsl:if test="1.13 >= 1.4">

Instead I opted for an approacah where I remove the integer part using substring-after() so that only the remainder is used in the comparison (assuming that the integer part is the same for now).

<xsl:if test="substring-after(1.13, ".") >= substring-after(1.4, ".")">

(actually I had to write it like this)

<xsl:if test="substring-after(1.13, &quot;.&quot;) >= substring-after(1.4, &quot;.&quot;)">

That works for the values 1.13 and 1.4. However, when I compared the values 1.10 and 1.4, the comparison fails.

Is there a better way of doing this comparison?

Upvotes: 3

Views: 1306

Answers (2)

Michael Kay
Michael Kay

Reputation: 163635

It depends on what XSLT processor and version you are using. With Saxon, you can do

  <xsl:template match="/" name="main">
     <out>
       <xsl:value-of select="compare('1.13', '1.4', 'http://saxon.sf.net/collation?alphanumeric=yes')"/>
     </out>
  </xsl:template>

which returns 1 indicating that the first argument is considered greater than the second. You could also write

<xsl:if test="'1.13' < '1.4'" 
        default-collation="http://saxon.sf.net/collation?alphanumeric=yes"/>

If you don't want to be dependent on processor-dependent collation URIs, then in XSLT 3.0 you could use the fn:sort() function which allows you to provide a function to compute your own sort key, which in this case might be tokenize($x, '\.')!xs:integer(.).

With earlier XSLT releases and no processor-dependent features, use xsl:sort with multiple sort keys computed using substring-before and substring-after.

Upvotes: 2

Aniket V
Aniket V

Reputation: 3247

You can try converting the versions to strings or integers and then perform a comparison. E.g. version 1.13 will be converted to 0113 and version 1.4 will be converted to 0104 and then 0113 will be compared with 0104.

Below XSLT uses XSLT 1.0 to perform the comparison for single . separator.

<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:template match="Versions">
        <xsl:variable name="oldVersion">
            <xsl:call-template name="convert-ver-to-string">
                <xsl:with-param name="ver" select="OldVersion"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="newVersion">
            <xsl:call-template name="convert-ver-to-string">
                <xsl:with-param name="ver" select="NewVersion"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="$newVersion &gt; $oldVersion">
                <Out>New greater than Old</Out>
            </xsl:when>
            <xsl:otherwise>
                <Out>Old greater than New</Out>
            </xsl:otherwise>
        </xsl:choose> 
    </xsl:template>

    <xsl:template name="convert-ver-to-string">
        <xsl:param name="ver" />
        <xsl:variable name="updVer">
            <xsl:choose>
                <xsl:when test="contains($ver, '.')">
                    <xsl:value-of select="$ver" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="concat($ver, '.0')" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:value-of select="format-number(substring-before($updVer, '.'), '00')" />
        <xsl:value-of select="format-number(substring-after($updVer, '.'), '00')" />
    </xsl:template>
</xsl:stylesheet>

Sample Input XML

<Versions>
    <OldVersion>1.3</OldVersion>
    <NewVersion>1.12</NewVersion>
</Versions>

Output

<Out>New greater than Old</Out>

Upvotes: 2

Related Questions