如何为Muenchian分组创建复合键并实现XML转CSV金额汇总
解决方案:动态生成AcctNo并分组汇总输出CSV
针对你遇到的问题——原XML无AcctNo字段、模板内变量无法用于Muenchian分组键,同时需要按Description+AcctNo汇总Amount输出CSV,分两种XSLT版本给出可行方案:
一、XSLT 1.0 解决方案(兼容旧版处理器)
XSLT 1.0中Muenchian分组需要全局可访问的节点集,因此先预处理所有记录,把动态生成的AcctNo附加到每个节点上,再基于处理后的节点集做分组:
步骤说明
- 预处理节点生成带AcctNo的临时节点集:遍历原XML记录,复制必要字段的同时,按规则生成AcctNo并添加到节点中
- 转换结果树片段为节点集:用扩展函数(如
exsl:node-set)把临时变量中的结果树片段转为可遍历的节点集 - 定义Muenchian分组键:用
Description和AcctNo的组合作为分组键 - 分组汇总并输出CSV:遍历每个唯一分组,汇总Amount后按CSV格式输出,注意处理特殊字符转义
代码示例
假设原XML结构如下:
<Transactions> <Transaction> <Description>Office Supplies</Description> <Amount>100.00</Amount> <Category>Admin</Category> </Transaction> <Transaction> <Description>Office Supplies</Description> <Amount>50.00</Amount> <Category>Admin</Category> </Transaction> <Transaction> <Description>Travel</Description> <Amount>200.00</Amount> <Category>Travel</Category> </Transaction> </Transactions>
对应的XSLT代码:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"> <!-- 预处理所有交易记录,添加生成的AcctNo --> <xsl:variable name="trans-with-acct"> <xsl:for-each select="Transactions/Transaction"> <Transaction> <xsl:copy-of select="Description, Amount"/> <AcctNo> <!-- 这里替换为你的AcctNo生成规则 --> <xsl:choose> <xsl:when test="Category='Admin'">1001</xsl:when> <xsl:when test="Category='Travel'">2001</xsl:when> <xsl:otherwise>9999</xsl:otherwise> </xsl:choose> </AcctNo> </Transaction> </xsl:for-each> </xsl:variable> <!-- 转换为可遍历的节点集 --> <xsl:variable name="trans-node-set" select="exsl:node-set($trans-with-acct)/Transaction"/> <!-- 定义分组键:Description + AcctNo的组合 --> <xsl:key name="trans-group" match="Transaction" use="concat(Description, '|', AcctNo)"/> <xsl:template match="/"> <!-- 输出CSV表头 --> <xsl:text>Description,AcctNo,TotalAmount </xsl:text> <!-- 遍历每个唯一分组 --> <xsl:for-each select="$trans-node-set[generate-id() = generate-id(key('trans-group', concat(Description, '|', AcctNo))[1])]"> <xsl:variable name="current-group" select="key('trans-group', concat(Description, '|', AcctNo))"/> <!-- 输出CSV行,处理双引号转义 --> <xsl:text>"</xsl:text> <xsl:value-of select="translate(Description, '"', '""')"/> <xsl:text>",</xsl:text> <xsl:value-of select="AcctNo"/> <xsl:text>,</xsl:text> <xsl:value-of select="sum($current-group/Amount)"/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:template> </xsl:stylesheet>
二、XSLT 2.0+ 解决方案(更简洁高效)
XSLT 2.0及以上版本支持<xsl:for-each-group>,可以直接在分组时动态计算AcctNo作为分组键,无需预处理节点集:
代码示例
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 复用AcctNo生成逻辑的命名模板 --> <xsl:template name="generate-acct-no"> <xsl:param name="category"/> <xsl:choose> <xsl:when test="$category='Admin'">1001</xsl:when> <xsl:when test="$category='Travel'">2001</xsl:when> <xsl:otherwise>9999</xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="/"> <!-- 输出CSV表头 --> <xsl:text>Description,AcctNo,TotalAmount </xsl:text> <!-- 按Description+AcctNo分组 --> <xsl:for-each-group select="Transactions/Transaction" group-by="concat(Description, '|', (call-template('generate-acct-no') with-param name='category' select=Category))"> <xsl:variable name="acct-no"> <xsl:call-template name="generate-acct-no"> <xsl:with-param name="category" select="current-group()[1]/Category"/> </xsl:call-template> </xsl:variable> <!-- 输出CSV行 --> <xsl:text>"</xsl:text> <xsl:value-of select="translate(current-group()[1]/Description, '"', '""')"/> <xsl:text>",</xsl:text> <xsl:value-of select="$acct-no"/> <xsl:text>,</xsl:text> <xsl:value-of select="sum(current-group()/Amount)"/> <xsl:text> </xsl:text> </xsl:for-each-group> </xsl:template> </xsl:stylesheet>
关键注意事项
- CSV格式兼容:如果
Description字段包含逗号或双引号,必须用双引号包裹字段,且双引号要转义为两个双引号(如translate(Description, '"', '""')) - AcctNo规则复用:把AcctNo生成逻辑封装为命名模板,方便维护和多处调用
- XSLT版本兼容性:如果使用旧版处理器(如MSXML),需用对应厂商的节点集扩展函数(如
msxsl:node-set)替代exsl:node-set
内容的提问来源于stack exchange,提问作者Greg Gregerson
相关产品推荐
相关产品推荐

