Subject: RE: max() of three xsl:number results
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Fri, 23 Dec 2005 09:20:56 -0000
|
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>
</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>
</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
|