[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

RE: max() of three xsl:number results

Subject: RE: max() of three xsl:number results
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Fri, 23 Dec 2005 09:20:56 -0000
xsl max
The data type of $X, $Y, and $Z is in each case document-node(). When you
atomize a document node the result is xdt:untypedAtomic. When you apply
max() to a untypedAtomic value, it is converted to a double, and "1.1.1"
cannot be converted to a double.

Change the variables to strings, and max() will then do a string comparison:

<xsl:variable name="X" as="xs:string">
   <xsl:number count="section" level="multiple" />
</xsl:variable>

This also has the advantage that a string is a much simpler object than a
document, so there is less overhead in constructing it and using it.

I would strongly recommend ALWAYS declaring the types of your variables and
parameters in XSLT 2.0. It really makes a big difference to ease of
debugging, especially when you use polymorphic functions like max().

However you still have a problem: the max of "1.2.1" and "1.10.3" is
"1.2.1". In principle you can define a collation that sorts 1.10.3 after
1.2.1, but that depends on the facilities offered by your XSLT processor
(your error message doesn't look like one from Saxon). A more pragmatic
solution might be to use <xsl:number format="00001"/> so that all components
of the section number are fixed-length.

Michael Kay
http://www.saxonica.com/

> -----Original Message-----
> From: Trevor Nicholls [mailto:trevor@xxxxxxxxxxxxxxxxxx] 
> Sent: 23 December 2005 05:05
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject:  max() of three xsl:number results
> 
> Hi
> 
> I have a <document> which consists of arbitrarily nested 
> <section>s of mixed
> content. Scattered throughout my document I have <target> 
> elements. I am
> trying to generate a listing which maps each <target> to its nearest
> <section>:
> a) if a target immediately precedes a section then I want 
> that section's
> location, otherwise
> b) I want the closest predecessor section, which may be
>  1) the section containing the target, or
>  2) the nearest section which precedes it.
> 
> In the following simple example I have given each section a 
> title which
> reflects its location in the structure, and given each target 
> a label which
> does the same.
> 
> <!-- ===== XML ===== -->
> <document>
>  <section title="1">
>   <section title="1.1">
>    <section title="1.1.1">
>     <section title="1.1.1.1">
>     </section>
>     <target label="1.1.1.1a" />
>    </section>
>    <target label="1.1.1.1b" />
>   </section>
>   <target label="1.2" />
>   <section title="1.2">
>   </section>
>   <section title="1.3">
>    <target label="1.3" />
>   </section>
>   <para />
>  </section>
>  <section title="2">
>   <target label="2" />
>   <para />
>  </section>
>  <section title="3">
>  </section>
>  <target label="3" />
> </document>
> <!-- ========== -->
> 
> The following stylesheet arrives at the numbers I want, viz:
> 
> <!-- ===== XSL 2.0 ===== -->
> <xsl:stylesheet version="2.0"
>    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> <xsl:output method="text" />
> 
> <xsl:template match="/">
> <xsl:apply-templates />
> </xsl:template>
> 
> <xsl:template match="target">
>  <xsl:variable name="X">
>   <xsl:number count="section" level="multiple" />
>  </xsl:variable>
>  <xsl:variable name="Y">
>   <xsl:number select="preceding::section[1]"
>      count="section" level="multiple" />
>  </xsl:variable>
>  <xsl:variable name="Z">
>   <xsl:choose>
>    <xsl:when test="following::*[1][self::section]">
>     <xsl:number select="following::*[1][self::section]"
>        count="section" level="multiple" />
>    </xsl:when>
>    <xsl:otherwise>
>     <xsl:value-of select="0" />
>    </xsl:otherwise>
>   </xsl:choose>
>  </xsl:variable>
> 
>  <xsl:value-of select="@label" />
>  <xsl:text> X</xsl:text><xsl:value-of select="$X">
>  <xsl:text> Y</xsl:text><xsl:value-of select="$Y">
>  <xsl:text> Z</xsl:text><xsl:value-of select="$Z">
>  <xsl:text>&#xa;</xsl:text>
> </xsl:template>
> <!-- ========== -->
> 
> When I apply this to the example XML above I get:
> 
> 1.1.1.1a  X1.1.1  Y1.1.1.1  Z0
> 1.1.1.1b  X1.1    Y1.1.1.1  Z0
> 1.2       X1      Y1.1.1.1  Z1.2
> 1.3       X1.3    Y1.2      Z0
> 2         X2      Y1.3      Z0
> 3         X       Y3        Z0
> 
> Clearly what I want is the (lexically) greatest value out of 
> X, Y and Z.
> But when I replace the target template's output with the 
> following lines:
> 
> <xsl:value-of select="@label" />
> <xsl:text> max </xsl:text>
> <xsl:value-of select="max(($X,$Y,$Z))" />
> <xsl:text>&#xa;</xsl:text>
> 
> I get an error:
> 
> Error in XPath 2.0 expression Invalid lexical value - '1.1.1'
> 
> What am I doing wrong?
> 
> Thanks
> Trevor

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.