RE: Calculations in XSL using multiple XML data fileds

Subject: RE: Calculations in XSL using multiple XML data fileds
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Wed, 1 Nov 2000 18:16:48 -0400 (EST)

>Thanks for that but I'm still a little unclear, I am calling the data in a
>table using the template:
><xsl:apply-templates select="last"/>
>and then applying the template using:
><xsl:template match="last">
>    <xsl:eval>format-number(q1/quotes/quote/last - (q2/quotes/quote/last div
>I have also seen the same function written as formatNumber() - which is
>correct (maybe both?)
>Would using the <xsl:apply-templates select="last"/> be correct as I am
>referring to multiple instances of the field "last" i.e.
>q1/quotes/quote/last and q2/quotes/quote/last :
>As this is not actually working (I am getting an ASP error) can I assume
>that the syntax is correct but that my XSLT processor is not

OK, there are two issues here.  The first is that xsl:eval is not an XSLT
1.0 instruction, so no compliant processor will understand it or use it.
To get the value of a function call inserted into your output tree, you
need xsl:value-of:

    select="format-number(q1/quotes/quote/last - 
                            (q2/quotes/quote/last div 7.45), '#,##0.00')" />

If you're using xsl:eval and it's working elsewhere then you're probably
using the old version of MSXML.  You should download the latest version
(September) from Microsoft
[] and have
a look at the MSXML FAQ [].
I guess that formatNumber is also a hangover from an old version of XSLT:
format-number() is the correct XSLT 1.0 function.  

Importantly, you should make sure that your xsl:stylesheet start tag
declares the correct namespace.  It should look something like:

<xsl:stylesheet version="1.0"

If you have the latest version of MSXML, and have the above start tag for
the xsl:stylesheet element, and things are *still* going wrong, then you
know there's something wrong with your XSLT.

The second issue is to do with the current node within the template.  Your
template is matching on a 'last' element, which means that within the
template, the current node is a 'last' element, and that all the XPaths
within the template are resolved relative to this current node.

So, with the XPaths that you currently have, the XSLT is expecting a tree
that looks like:

+- element: last               <- current node
   +- element: q1              <- q1
   |  +- element: quotes       <- q1/quotes
   |     +- element: quote     <- q1/quotes/quote
   |        +- element: last   <- q1/quotes/quote/last
   +- element: q2              <- q2
      +- element: quotes       <- q2/quotes
         +- element: quote     <- q2/quotes/quote
            +- element: last   <- q2/quotes/quote/last

I don't know what your tree actually looks like, but let's say that the
'last' element you're applying templates to is the 'last' element within
q1/quotes/quote and that q1 and q2 are siblings.  In that case, the tree
looks like:

+- element: foo                <- ancestor::foo
   +- element: q1              <- ancestor::q1
   |  +- element: quotes       <- ancestor::quotes
   |     +- element: quote     <- parent::quote
   |        +- element: last   <- current node
   +- element: q2              <- ancestor::foo/q2
      +- element: quotes       <- ancestor::foo/q2/quotes
         +- element: quote     <- ancestor::foo/q2/quotes/quote
            +- element: last   <- ancestor::foo/q2/quotes/quote/last

So, if the template is being applied to the 'last' element within
q1/quotes/quote then you can get the calculated value with:

    select="format-number(. - (ancestor::foo/q2/quotes/quote/last div 7.45), 
                          '#,##0.00')" />

If it's being applied to the 'last' element within q2/quotes/quote then you
can get the calculated value with:

    select="format-number(ancestor::foo/q1/quotes/quote/last - (. div 7.45),
                          '#,##0.00')" />

Depending on your source document, it may be worth plucking the values out
directly from the root node rather than navigating from one to the other by
going up and down the tree like this, so something like:

    select="format-number(/foo/q1/quotes/quote/last - 
                          (/foo/q2/quotes/quote/last div 7.45),
'#,##0.00')" />

I'm not sure what the current node is when you apply templates to the
'last' element, but note that unless you override it somehow, it's likely
that *both* 'last' elements are going to have the template applied to them,
and that you will get the calculated value twice.  That may be what you
want, but you do need to make sure that the template can cater for
whichever occurance of 'last' is having templates applied to it (which will
usually mean using absolute paths from the root node) to get to the
relevant values.  Usually in circumstances where it doesn't matter what the
current node is, I'd use a named template rather than applying templates:

  <xsl:call-template name="insert-calculated-value" />


<xsl:template name="insert-calculated-value">
    select="format-number(/foo/q1/quotes/quote/last - 
                          (/foo/q2/quotes/quote/last div 7.45),
'#,##0.00')" />

I'm sorry that this is a bit vague, but it's really difficult to give
accurate advice without knowing what the source document looks like, and
having an idea about the context in which the xsl:apply-templates is being
called.  Please feel free to post more details if the above isn't hitting
the spot :)



Jeni Tennison

 XSL-List info and archive:

Current Thread