我有以下XML源结构:
<turnovers>
<turnover repid="1" amount="500" rate="0.1"/>
<turnover repid="5" amount="600" rate="0.5"/>
<turnover repid="4" amount="400" rate="0.2"/>
<turnover repid="1" amount="700" rate="0.05"/>
<turnover repid="2" amount="100" rate="0.15"/>
<turnover repid="1" amount="900" rate="0.25"/>
<turnover repid="2" amount="1000" rate="0.18"/>
<turnover repid="5" amount="200" rate="0.55"/>
<turnover repid="9" amount="700" rate="0.40"/>
</turnovers>
我需要一个XSL:value-of select语句,它将返回给定代表ID的rate属性和amount属性的乘积之和。所以对于代表5,我需要((600 x 0.5)+(200 x 0.55))。
答案 0 :(得分:9)
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/turnovers">
<val>
<!-- call the sum function (with the relevant nodes) -->
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="turnover[@repid='5']" />
</xsl:call-template>
</val>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="nodes" />
<xsl:param name="sum" select="0" />
<xsl:variable name="curr" select="$nodes[1]" />
<!-- if we have a node, calculate & recurse -->
<xsl:if test="$curr">
<xsl:variable name="runningsum" select="
$sum + $curr/@amount * $curr/@rate
" />
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="sum" select="$runningsum" />
</xsl:call-template>
</xsl:if>
<!-- if we don't have a node (last recursive step), return sum -->
<xsl:if test="not($curr)">
<xsl:value-of select="$sum" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
给出:
<val>410</val>
两个<xsl:if>
可以替换为单个<xsl:choose>
。这意味着在递归期间少检查一次,但这也意味着另外两行代码。
答案 1 :(得分:2)
在普通的XSLT 1.0中,您需要一个递归模板,例如:
<xsl:template match="turnovers">
<xsl:variable name="selectedId" select="5" />
<xsl:call-template name="sum_turnover">
<xsl:with-param name="turnovers" select="turnover[@repid=$selectedId]" />
</xsl:call-template>
</xsl:template>
<xsl:template name="sum_turnover">
<xsl:param name="total" select="0" />
<xsl:param name="turnovers" />
<xsl:variable name="head" select="$turnovers[1]" />
<xsl:variable name="tail" select="$turnovers[position()>1]" />
<xsl:variable name="calc" select="$head/@amount * $head/@rate" />
<xsl:choose>
<xsl:when test="not($tail)">
<xsl:value-of select="$total + $calc" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sum_turnover">
<xsl:with-param name="total" select="$total + $calc" />
<xsl:with-param name="turnovers" select="$tail" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
答案 2 :(得分:1)
这应该可以解决问题,你需要做一些进一步的工作来选择不同的repid
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="totals">
<product>
<xsl:for-each select="turnovers/turnover">
<repid repid="{@repid}">
<value><xsl:value-of select="@amount * @rate"/></value>
</repid>
</xsl:for-each>
</product>
</xsl:variable>
<totals>
<total repid="5" value="{sum($totals/product/repid[@repid='5']/value)}"/>
</totals>
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:1)
在XSLT 1.0中,使用FXSL可以轻松解决此类问题:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="xsl f ext"
>
<xsl:import href="zipWith.xsl"/>
<xsl:output method="text"/>
<xsl:variable name="vMultFun" select="document('')/*/f:mult-func[1]"/>
<xsl:template match="/">
<xsl:call-template name="profitForId"/>
</xsl:template>
<xsl:template name="profitForId">
<xsl:param name="pId" select="1"/>
<xsl:variable name="vrtfProducts">
<xsl:call-template name="zipWith">
<xsl:with-param name="pFun" select="$vMultFun"/>
<xsl:with-param name="pList1" select="/*/*[@repid = $pId]/@amount"/>
<xsl:with-param name="pList2" select="/*/*[@repid = $pId]/@rate"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ext:node-set($vrtfProducts)/*)"/>
</xsl:template>
<f:mult-func/>
<xsl:template match="f:mult-func" mode="f:FXSL">
<xsl:param name="pArg1"/>
<xsl:param name="pArg2"/>
<xsl:value-of select="$pArg1 * $pArg2"/>
</xsl:template>
</xsl:stylesheet>
在最初发布的源XML文档上应用此转换时,会生成正确的结果:
<强> 310 强>
在XSLT 2.0中,使用FXSL 2.0的相同解决方案可以用XPath单行表示:
sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.),
/*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.)
)
)
整个转型:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="f xs"
>
<xsl:import href="../f/func-zipWithDVC.xsl"/>
<xsl:import href="../f/func-Operators.xsl"/>
<!-- To be applied on testFunc-zipWith4.xml -->
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.),
/*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.)
)
)
"/>
</xsl:template>
</xsl:stylesheet>
同样,这种转变产生了正确的答案:
<强> 310 强>
请注意以下:
f:zipWith()
函数将函数fun()
(两个参数)和两个具有相同长度的项列表作为参数。它生成一个相同长度的新列表,其项目是fun()
在两个列表的相应k
项中成对应用的结果。
f:zipWith()
在表达式中使用函数f:multiply()
和两个相应的“ammount
”和“rate
”属性序列。 sesult是一个序列,其中每个项目都是相应的“ammount
”和“rate
”的乘积。
最后,生成此序列的 sum 。
不需要编写显式递归,并且还保证f:zipWith()
中使用的幕后递归永远不会崩溃(对于所有实际情况)“堆栈溢出”
答案 4 :(得分:0)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:variable name="repid" select="5" />
<xsl:template match="/">
<xsl:value-of select=
"sum(for $x in /turnovers/turnover[@repid=$repid] return $x/@amount * $x/@rate)"/>
</xsl:template>
</xsl:stylesheet>
如果您只需要值而不是xml,则可以执行此操作。
答案 5 :(得分:-2)
在XSLT中执行此操作的最简单方法可能是使用编程语言绑定,以便您可以定义自己的XPath函数。