如何使用XSLT 1.0计算XML中numbers元素的数值总和?
解决XSLT 1.0中逗号分隔数字的求和问题
嘿,我来帮你搞定这个问题~你之前用str:tokenize()没得到预期结果,核心原因是这个函数是XSLT 2.0及更高版本才支持的,XSLT 1.0根本没有这个内置函数,所以得换个1.0兼容的实现方式。
下面给你两种可行的解决方案,都是XSLT 1.0原生支持的:
方法一:递归拆分字符串+节点集求和
这种方法先把逗号分隔的字符串拆成独立的数字节点,再用sum()函数计算总和。适合需要保留拆分后数字的场景,也方便扩展其他操作。
完整XSLT代码:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common"> <xsl:output method="xml" indent="yes"/> <!-- 递归模板:拆分逗号分隔字符串,生成<number>节点 --> <xsl:template name="split-numbers"> <xsl:param name="input-string"/> <xsl:choose> <!-- 还有逗号,拆分第一个数字后递归处理剩余部分 --> <xsl:when test="contains($input-string, ',')"> <number> <xsl:value-of select="normalize-space(substring-before($input-string, ','))"/> </number> <xsl:call-template name="split-numbers"> <xsl:with-param name="input-string" select="substring-after($input-string, ',')"/> </xsl:call-template> </xsl:when> <!-- 处理最后一个数字(或单个数字) --> <xsl:otherwise> <number> <xsl:value-of select="normalize-space($input-string)"/> </number> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 主模板:遍历用户信息并计算总和 --> <xsl:template match="/"> <user-number-sums> <xsl:for-each select="documents/document/userData/userInfo"> <user> <name><xsl:value-of select="user"/></name> <total> <!-- 调用拆分模板生成数字节点,再转换为节点集求和 --> <xsl:variable name="number-nodes"> <xsl:call-template name="split-numbers"> <xsl:with-param name="input-string" select="numbers"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="sum(exsl:node-set($number-nodes)/number)"/> </total> </user> </xsl:for-each> </user-number-sums> </xsl:template> </xsl:stylesheet>
关键细节:
normalize-space():处理数字前后可能存在的空格(如果你的数据里有的话),避免求和出错。exsl:node-set():XSLT 1.0中,变量里的临时节点不能直接被sum()访问,需要用这个EXSLT函数把它转换成可遍历的节点集。大部分主流XSLT处理器(比如Saxon、Xalan)都支持这个扩展。
方法二:递归直接求和(无需节点集)
如果不需要保留拆分后的数字,只想直接得到总和,这种方法更高效,也不需要依赖EXSLT扩展。
核心是写一个递归模板,每次拆分出一个数字并累加到总和里:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <!-- 递归模板:直接计算逗号分隔字符串的数字总和 --> <xsl:template name="compute-sum"> <xsl:param name="input-string"/> <xsl:param name="running-total" select="0"/> <xsl:choose> <xsl:when test="contains($input-string, ',')"> <!-- 拆分第一个数字,累加到总和后递归 --> <xsl:call-template name="compute-sum"> <xsl:with-param name="input-string" select="substring-after($input-string, ',')"/> <xsl:with-param name="running-total" select="$running-total + number(normalize-space(substring-before($input-string, ',')))"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <!-- 加上最后一个数字,返回最终总和 --> <xsl:value-of select="$running-total + number(normalize-space($input-string))"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 主模板:调用求和模板 --> <xsl:template match="/"> <user-number-sums> <xsl:for-each select="documents/document/userData/userInfo"> <user> <name><xsl:value-of select="user"/></name> <total> <xsl:call-template name="compute-sum"> <xsl:with-param name="input-string" select="numbers"/> </xsl:call-template> </total> </user> </xsl:for-each> </user-number-sums> </xsl:template> </xsl:stylesheet>
测试结果
用你提供的XML输入,两种方法都会输出正确的结果:
<user-number-sums> <user> <name>John</name> <total>0</total> </user> <user> <name>Susan</name> <total>16</total> </user> </user-number-sums>
内容的提问来源于stack exchange,提问作者ottrin
相关产品推荐
相关产品推荐

