I have a variable which I set the value. Thereafter, in a loop, I would like to edit this variable and use it in the next iteration. Is there any way I can edit the value in the variable? Thanks!
<xsl:variable name="numberVariable" select="5">
----loop----
$numberVariable = $numberVariable+2
----End loop----
in a loop, I would like to edit this variable and use it in the next iteration. Is there any way I can edit the value in the variable?
The answer is negative:
XSLT is a functional language and this, among other things means that a variable's value, once defined, is immutable.
One can achieve the same effect in a more safe way, by calling another callable unit of the language (template or function) and passing the wanted new value as an argument.
I. Here is a simple example. The following transformation calculates the factorial of the integer, obtained from the string value of the document (top) element of the source XML document:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*/text()" name="factorial">
<xsl:param name="pN" select="."/>
<xsl:param name="pAccum" select="1"/>
<xsl:value-of select="substring($pAccum, 1 div not($pN > 1))"/>
<xsl:apply-templates select="self::node()[$pN > 1]">
<xsl:with-param name="pN" select="$pN -1"/>
<xsl:with-param name="pAccum" select="$pAccum * $pN"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following source XML document:
<t>9</t>
the wanted, correct result is produced:
362880
In the code above, we see how the value of the parameter $pN is "decreased" from call to call, until it reaches 1, and the value of the parameter $pAccum is multiplied on each call by the value of the parameter $pN.
Do note, however, that we do not modify any parameter at all -- on each call a new instance of the parameter(s) is created, having the same name(s), but living only in the inner scope (call).
II. Often we can avoid the need for recursion: The following XSLT 1.0 transformation calculates and outputs the cubes of the numbers from 1 to 20:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDoc" select="document('')"/>
<xsl:template match="/*" name="cubes">
<xsl:param name="pN" select="20"/>
<xsl:for-each select=
"($vDoc//node() | $vDoc//node()/#* | $vDoc//namespace::*)
[not(position() > $pN)]">
<xsl:variable name="vM" select="position()"/>
<xsl:value-of select="$vM*$vM*$vM"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any source XML document (not used), the wanted, correct result is produced:
1
8
27
64
125
216
343
512
729
1000
1331
1728
2197
2744
3375
4096
4913
5832
6859
8000
Do note how the standard XPath function position() is called to produce the "loop counter" :). This is the well known method of Piez.
Starting from XSLT versions 2.0 and above, no such tricks are necessary. One can simply write the following, using an XPath 2.0 range expression:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDoc" select="document('')"/>
<xsl:template match="/*" name="cubes">
<xsl:param name="pN" select="20"/>
<xsl:for-each select="1 to $pN">
<xsl:value-of select=". * . *."/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Look into the xsl:number element- you can do something like this:
<xsl:variable name="numberVariable">
<xsl:number/>
</xsl:variable>
<xsl:value-of select="$numberVariable * 2"/>
It's difficult to know exactly what you're doing from your question, but hopefully this can point you in the right direction.
Related
I know this is an old question that has been passed around SO several times but I was wondering whether anyone can expand on whether a URL that has a querystring attached to it can be stripped out via XSLT 1.0 and can be used as a parameter for later use of the XSLT transformation.
For example, I have a URL of http://www.mydomain.com/mypage.htm?param1=a¶m2=b
via XSLT, I am looking for a result of something along the lines of:
<xsl:param name="param1">a</xsl:param> and <xsl:param name="param2">b</xsl:param>
where both parameter name (param1, param2) and it's value (a, b) has been extracted from the quesrystring to allow me to use $param1 and $param2 later on say in an if condition
e.g. <xsl:if test="$param1 = 'a'> comes out true but if we use <xsl:if test="$param1 = 'b'> comes out false.
I have seen a similar question here: Retrieve page URL params or page URL in XSLT which uses the str-split-to-words template but I have unsuccessfully got it working (possibly due to me implementing it the wrong way) so any working examples of how it can be done in practice would be massively beneficial.
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common">
<xsl:import href="http://fxsl.cvs.sourceforge.net/viewvc/fxsl/fxsl-xslt2/f/strSplit-to-Words.xsl"/>
<xsl:output indent="yes" method="html"/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pQString"/>
<xsl:with-param name="pDelimiters" select="'?&'"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:template>
<xsl:template match="word">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
There are a few problems in your code:
<xsl:import href="http://fxsl.cvs.sourceforge.net/viewvc/fxsl/fxsl-xslt2/f/strSplit-to-Words.xsl"/> I doubt that the wanted stylesheet can be imported directly from its SourceForge view page -- especially taking into account, that it itself imports other FXSL stylesheets. The correct way to use FXSL is to download it to the local computer and reference its stylesheets off the file location it resides in at the local computer.
...
.2. <xsl:with-param name="pStr" select="$pQString"/> This will produce a compilation error because you missed to define the $pQString global/external parameter. You need to define this parameter at global level. It can be given a default value (for example a particular URL) for easier testing. However, the idea of using this parameter is that the invoker of the transformation should pass this parameter to the transformation.
.3. The results of the transformation are written to the output. While this is good for demonstration purposes, you want to be able to use these results later in the transformation. The way to do this is to capture these results in a variable, make another variable from it, with a regular tree (from its RTF type) and then reference the nodes of this last variable.
Here is an example of the code you want (provided that you have downloaded FXSL, unzipped the distribution and saved this code in the same directory as the unzipped distribution of FXSL):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="pUrl" select=
"'http://www.mydomain.com/mypage.htm?param1=a¶m2=b'"/>
<xsl:param name="pQString" select=
"substring-after($pUrl, '?')"
/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pQString"/>
<xsl:with-param name="pDelimiters"
select="'?&'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vrtfqueryParams">
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:variable>
<xsl:variable name="vqueryParams" select="ext:node-set($vrtfqueryParams)/*"/>
<xsl:value-of select="$vqueryParams/#name[. ='param1']"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="$vqueryParams[#name = 'param1']"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vqueryParams/#name[. ='param2']"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="$vqueryParams[#name = 'param2']"/>
</xsl:template>
<xsl:template match="word">
<param name="{substring-before(.,'=')}">
<xsl:value-of select="substring-after(.,'=')"/>
</param>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used in this demo), the wanted, correct result -- the query-string parameters referenced of a results variable by name -- is produced:
param1 : a
param2 : b
I am learning xslt and had one question about how can i use xslt variable in diff. for each loop. I know xslt isn't a procedural language so variable declared in for loop cannot be accessed in another loop. But is there any way I can just declare global variable then assign some value in first for loop and use that variable in second for loop?
Any ideas would be highly appreciated.
Thanks
is there any way I can just declare global variable then assign some
value in first for loop and use that variable in second for loop?
The way to assign value to an xsl:variable (of course this is only initialization) from within an xsl:for-each, is to include the xsl:for-each in the body of the variable.
Here is a complete example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:variable name="vMax">
<xsl:for-each select="num">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
Values close to the maximum:
<xsl:text/>
<xsl:for-each select="num">
<xsl:if test="not($vMax - . > 3) ">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document...
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
...it first defines a vMax variable that gets its value from the xsl:for-each contained in its body.
Then the vMax variable is used in the second xsl:for-each to output all numbers that are "close" to the so computed maximum.
The wanted, correct result is produced:
Values close to the maximum:
07
08
09
10
It is also possible to simulate "assigning" a variable with different values by using a recursively called named template and pass the "new value" as parameter to the called template.
Here is an example showing this technique. Here we are calculating the maximum of values, contained in nodes of a node-set. Every time we access the next node in the node-set, the current maximum is compared to this value and if necessary the new maximum becomes the value of the next node. We then call the same template recursively, passing as the value of the current maximum the new maximum:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:call-template name="max">
<xsl:with-param name="pList" select="*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="max">
<xsl:param name="pMax" select="-99999999"/>
<xsl:param name="pList"/>
<xsl:choose>
<xsl:when test="$pList[1]">
<xsl:variable name="vnewMax" select=
"$pMax * ($pMax >= $pList[1])
+
$pList[1] * not($pMax >= $pList[1])
"/>
<xsl:call-template name="max">
<xsl:with-param name="pMax" select="$vnewMax"/>
<xsl:with-param name="pList" select="$pList[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pMax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the same XML document (above), the wanted, correct result is produced:
10
xsl:for-each is not a loop in the sense for or foreach loops exist in procedural languages so any question talking about loops is difficult to understand and more difficult to answer.
If you want to use global variables in XSLT you can do so but you would bind a value to the variable where you declare it (i.e. globally), you can't assign a value later on in a for-each as you seem to want to do.
you have written, 'XSL is a procedural lang' .. Well, its not. It is a Declarative Language ..
Variable is assigned along with its declaration, variables don't change!
Usually we follow recursive call for templates using call-template passing params to them .. (this works like recursive function calling with passing arguments in procedural languages)
That is one method to handle counts and conditional looping etc ..
We would be happy to help you incase if you mention the exact scenario with Sample XML, and the output you are expecting out of it :)
Is it possible to store the output of an XSL transformation in some sort of variable and then perform an additional transformation on the variable's contents? (All in one XSL file)
(XSLT-2.0 Preferred)
XSLT 2.0 Solution :
<xsl:variable name="firstPassResult">
<xsl:apply-templates select="/" mode="firstPass"/>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="$firstPassResult" mode="secondPass"/>
</xsl:template>
The trick here, is to use mode="firstPassResult" for the first pass while all the templates for the sedond pass should have mode="secondPass".
Edit:
Example :
<root>
<a>Init</a>
</root>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="firstPassResult">
<xsl:apply-templates select="/" mode="firstPass"/>
</xsl:variable>
<xsl:template match="/" mode="firstPass">
<test>
<firstPass>
<xsl:value-of select="root/a"/>
</firstPass>
</test>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$firstPassResult" mode="secondPass"/>
</xsl:template>
<xsl:template match="/" mode="secondPass">
<xsl:message terminate="no">
<xsl:copy-of select="."/>
</xsl:message>
</xsl:template>
</xsl:stylesheet>
Output :
[xslt] <test><firstPass>Init</firstPass></test>
So the first pass creates some elements with the content of root/a and the second one prints the created elements to std out. Hopefully this is enough to get you going.
Yes, with XSLT 2.0 it is easy. With XSLT 1.0 you can of course also use modes and store a temporary result in a variable the same way as in XSLT 2.0 but the variable is then a result tree fragment, to be able to process it further with apply-templates you need to use an extension function like exsl:node-set on the variable.
using pure XSLT 1.0, how can I conditionally assign the node. I am trying something like this but it's not working.
<xsl:variable name="topcall" select="//topcall"/>
<xsl:variable name="focusedcall" select="//focusedcall" />
<xsl:variable name="firstcall" select="$topcall | $focusedcall"/>
For variable firstcall, I am doing the conditional node selection. if there is a topcall then assign it to firstcall, othersie assign firstcall to the focusedcall.
This should work:
<xsl:variable name="firstcall" select="$topcall[$topcall] |
$focusedcall[not($topcall)]" />
In other words, select $topcall if $topcall nodeset is non-empty; $focusedcall if $topcall nodeset is empty.
Re-Update regarding "it can be 5-6 nodes":
Given that there may be 5-6 alternatives, i.e. 3-4 more besides $topcall and $focusedcall...
The easiest solution is to use <xsl:choose>:
<xsl:variable name="firstcall">
<xsl:choose>
<xsl:when test="$topcall"> <xsl:copy-of select="$topcall" /></xsl:when>
<xsl:when test="$focusedcall"><xsl:copy-of select="$focusedcall" /></xsl:when>
<xsl:when test="$thiscall"> <xsl:copy-of select="$thiscall" /></xsl:when>
<xsl:otherwise> <xsl:copy-of select="$thatcall" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
However, in XSLT 1.0, this will convert the output of the chosen result to a result tree fragment (RTF: basically, a frozen XML subtree). After that, you won't be able to use any significant XPath expressions on $firstcall to select things from it. If you need to do XPath selections on $firstcall later, e.g. select="$firstcall[1]", you then have a few options...
Put those selections into the <xsl:when> or <xsl:otherwise> so that they happen before the data gets converted to an RTF. Or,
Consider the node-set() extension, which converts an RTF to a nodeset, so you can do normal XPath selections from it. This extension is available in most XSLT processors but not all. Or,
Consider using XSLT 2.0, where RTFs are not an issue at all. In fact, in XPath 2.0 you can put normal if/then/else conditionals inside the XPath expression if you want to.
Implement it in XPath 1.0, using nested predicates like
:
select="$topcall[$topcall] |
($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])[not($topcall)]"
and keep on nesting as deep as necessary. In other words, here I took the XPath expression for 2 alternatives above, and replaced $focusedcall with
($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])
The next iteration, you would replace $thiscall with
($thiscall[$thiscall] | $thatcall[not($thiscall)])
etc.
Of course this becomes hard to read, and error-prone, so I would not choose this option unless the others aren't feasible.
Does <xsl:variable name="firstcall" select="($topcall | $focusedcall)[1]"/> do what you want? That is usually the way to take the first node in document order of different types of nodes.
I. XSLT 1.0 Solution This short (30 lines), simple and parameterized transformation works with any number of node types/names:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pRatedCalls">
<call type="topcall"/>
<call type="focusedcall"/>
<call type="normalcall"/>
</xsl:param>
<xsl:variable name="vRatedCalls" select=
"document('')/*/xsl:param[#name='pRatedCalls']/*"/>
<xsl:variable name="vDoc" select="/"/>
<xsl:variable name="vpresentCallNames">
<xsl:for-each select="$vRatedCalls">
<xsl:value-of select=
"name($vDoc//*[name()=current()/#type][1])"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select=
"//*[name()
=
substring-before(normalize-space($vpresentCallNames),' ')]"/>
</xsl:template>
</xsl:stylesheet>
When applied to this XML document (do note the document order doesn't coincide with the specified priorities in the pRatedCalls parameter):
<t>
<normalcall/>
<focusedcall/>
<topcall/>
</t>
produces exactly the wanted, correct result:
<topcall/>
when the same transformation is applied to the following XML document:
<t>
<normalcall/>
<focusedcall/>
</t>
again the wanted and correct result is produced:
<focusedcall/>
Explanation:
The names of the nodes that are to be searched for (as many as needed and in order of priority) are specified by the global (typically externally specified) parameter named $pRatedCalls.
Within the body of the variable $vpresentCallNames we generate a space-separated list of names of elements that are both specified as a value of the type attribute of a call elementin the$pRatedCalls` parameter and also are names of elements in the XML document.
Finally, we determine the first such name in this space-separated list and select all elements in the document, that have this name.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pRatedCalls" select=
"'topcall', 'focusedcall', 'normalcall'"/>
<xsl:template match="/">
<xsl:sequence select=
"//*
[name()=$pRatedCalls
[. = current()//*/name()]
[1]
]"/>
</xsl:template>
</xsl:stylesheet>
I am having a difficult time trying to do something that seems like it should be really easy to do. I basically want to multiply 2 numbers in a node and then sum the total of those numbers for all the nodes. Here is the XSLT code I have tried.
<xsl:value-of select="sum(Parts/Part/Quantity * Parts/Part/Rate)"/>
This code results in an error that says "Argument 1 of function sum cannot be converted to a node set."
Does anyone have an idea of what is wrong or how I can accomplish what I am trying to do?
Here are three possible solutions:
Solution1 XSLT2:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select="sum(/*/*/(rate * quantity))"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<parts>
<part>
<rate>0.37</rate>
<quantity>10</quantity>
</part>
<part>
<rate>0.03</rate>
<quantity>10</quantity>
</part>
</parts>
The wanted result is produced:
4
The XSLT 2.0 solution uses the fact that in XPath 2.0 it is allowed that the right argument of the last "/" operator can be an expression or generally a function. This expression/function is applied for each of the nodes selected so far acting as the context node, and each function application produces one result.
Solution2 XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="sumProducts">
<xsl:param name="pList"/>
<xsl:param name="pAccum" select="0"/>
<xsl:choose>
<xsl:when test="$pList">
<xsl:variable name="vHead" select="$pList[1]"/>
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="$pList[position() > 1]"/>
<xsl:with-param name="pAccum"
select="$pAccum + $vHead/rate * $vHead/quantity"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pAccum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied on the above XML document, the correct result is produced:
4
This is a typical XSLT 1.0 recursive solution. Do note how the sumProducts template calls itself recursively, until the entire input list, passed in the parameter $pList is processed.
Solution3 FXSL (XSLT 1.0):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:test-map-product="test-map-product"
exclude-result-prefixes="xsl ext test-map-product"
>
<xsl:import href="sum.xsl"/>
<xsl:import href="map.xsl"/>
<xsl:import href="product.xsl"/>
<!-- This transformation is to be applied on:
salesMap.xml
It contains the code of the "sum of products" from the
article "The Functional Programming Language XSLT"
-->
<test-map-product:test-map-product/>
<xsl:output method="text"/>
<xsl:template match="/">
<!-- Get: map product /sales/sale -->
<xsl:variable name="vSalesTotals">
<xsl:variable name="vTestMap" select="document('')/*/test-map-product:*[1]"/>
<xsl:call-template name="map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pList1" select="/sales/sale"/>
</xsl:call-template>
</xsl:variable>
<!-- Get sum map product /sales/sale -->
<xsl:call-template name="sum">
<xsl:with-param name="pList" select="ext:node-set($vSalesTotals)/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="makeproduct" match="*[namespace-uri() = 'test-map-product']">
<xsl:param name="arg1"/>
<xsl:call-template name="product">
<xsl:with-param name="pList" select="$arg1/*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<sales>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
</sales>
The correct result is produced:
7.5600000000000005
In the last case for each sale we calculate the product of price, quantity and all available (variable number of) discount-s.
FXSL is a pure XSLT implementation of higher order functions. In this example the higher-order function f:map() is used to map the function f:product() on the list of children of every sale element. Then the results are summed to produce the final result.
All of Dimitre's solutions work and he's right that you don't need to use extension functions but sometimes it makes life easier. It's not too harmful, especially when you use exslt extensions which are supported across multiple XSLT processors. Also, the reason you're getting the sequence errors is probably because you're using an XSLT 1 processor.
If you want to persist with your chosen solution, you'll need to use Saxon or some other XSLT processor that supports XSLT 2.
Otherwise, here's an alternative method of doing it in XSLT 1. This will work in most XSLT processors and some peope might find it easier to grok than the recursive version. Personally, I prefer the recursive version (Dimitre's 3rd proposal) because it is more portable.
<xsl:stylesheet version="1.0"
xmlns:ex="http://exslt.org/common"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template name="GetProducts">
<xsl:param name="left"/>
<xsl:param name="right"/>
<xsl:for-each select="$left/text()">
<product>
<xsl:value-of select="number(.) * number($right[position()])"/>
</product>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="products">
<xsl:call-template name="GetProducts">
<xsl:with-param name="left" select="Parts/Part/Rate"/>
<xsl:with-param name="right" select="Parts/Part/Quantity"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ex:node-set($products)/product)"/>
</xsl:template>
</xsl:stylesheet>