如何在XSLT转换中按两个节点分组并统计对应数值总和
XSLT双层分组统计实现方案
问题背景
需要对给定的库存XML数据进行双层分组统计,输出符合Demandware库存接口规范的XML文件。
输入XML示例
<?xml version="1.0" encoding="utf-8"?> <Document> <TRU_SALESFORCEONHAND> <INVENTDIMID>#0000000150000BBB</INVENTDIMID> <INVENTLOCATIONID>W2472-Mezz</INVENTLOCATIONID> <INVENTSITEID>DC</INVENTSITEID> <ITEMID>000003</ITEMID> <ONHANDQTY>11.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> <TRU_SALESFORCEONHAND> <INVENTDIMID>#0000000150000BBC</INVENTDIMID> <INVENTLOCATIONID>W2472-Mezz</INVENTLOCATIONID> <INVENTSITEID>DC</INVENTSITEID> <ITEMID>000003</ITEMID> <ONHANDQTY>3.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> <TRU_SALESFORCEONHAND> <INVENTDIMID>#000000015000119A</INVENTDIMID> <INVENTLOCATIONID>W3501</INVENTLOCATIONID> <INVENTSITEID>DC</INVENTSITEID> <ITEMID>000004</ITEMID> <ONHANDQTY>23.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> <TRU_SALESFORCEONHAND> <INVENTDIMID>#00000001500011AD</INVENTDIMID> <INVENTLOCATIONID>W3508</INVENTLOCATIONID> <INVENTSITEID>Site-ON</INVENTSITEID> <ITEMID>000003</ITEMID> <ONHANDQTY>1.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> <TRU_SALESFORCEONHAND> <INVENTDIMID>#00000001500011AE</INVENTDIMID> <INVENTLOCATIONID>W3508</INVENTLOCATIONID> <INVENTSITEID>Site-ON</INVENTSITEID> <ITEMID>000004</ITEMID> <ONHANDQTY>8.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> <TRU_SALESFORCEONHAND> <INVENTDIMID>#0000000150001A73</INVENTDIMID> <INVENTLOCATIONID>In_Transit</INVENTLOCATIONID> <INVENTSITEID>DC</INVENTSITEID> <ITEMID>000004</ITEMID> <ONHANDQTY>23.000000</ONHANDQTY> </TRU_SALESFORCEONHAND> </Document>
目标输出XML示例
<?xml version="1.0" encoding="UTF-8"?> <inventory xmlns="http://www.demandware.com/xml/impex/inventory/2007-05-31"> <inventory-list> <header list-id="DC"> <timestamp>2021-02-01T14:37:15.494Z</timestamp> </header> <records> <record product-id="000003"> <allocation>14.000000</allocation> <allocation-timestamp>2021-02-01T14:37:15.494Z</allocation-timestamp> </record> <record product-id="000004"> <allocation>23.000000</allocation> <allocation-timestamp>2021-02-01T14:37:15.494Z</allocation-timestamp> </record> </records> </inventory-list> <inventory-list> <header list-id="Site-ON"> <timestamp>2021-02-01T14:37:15.494Z</timestamp> </header> <records> <record product-id="000003"> <allocation>1.000000</allocation> <allocation-timestamp>2021-02-01T14:37:15.494Z</allocation-timestamp> </record> <record product-id="000004"> <allocation>31.000000</allocation> <allocation-timestamp>2021-02-01T14:37:15.494Z</allocation-timestamp> </record> </records> </inventory-list> </inventory>
实现代码
XSLT 2.0 版本(推荐,语法简洁)
直接使用内置的for-each-group语法实现多层分组,适配绝大多数现代XSLT处理器。
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.demandware.com/xml/impex/inventory/2007-05-31"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 获取当前时间戳,全局复用 --> <xsl:variable name="currentTime" select="current-dateTime()"/> <xsl:template match="/Document"> <inventory> <!-- 第一层分组:按INVENTSITEID分组 --> <xsl:for-each-group select="TRU_SALESFORCEONHAND" group-by="INVENTSITEID"> <inventory-list> <header list-id="{current-grouping-key()}"> <timestamp><xsl:value-of select="$currentTime"/></timestamp> </header> <records> <!-- 第二层分组:同一站点下按ITEMID分组 --> <xsl:for-each-group select="current-group()" group-by="ITEMID"> <record product-id="{current-grouping-key()}"> <allocation> <!-- 统计当前分组下ONHANDQTY总和,保留6位小数 --> <xsl:value-of select="format-number(sum(current-group()/ONHANDQTY), '0.000000')"/> </allocation> <allocation-timestamp><xsl:value-of select="$currentTime"/></allocation-timestamp> </record> </xsl:for-each-group> </records> </inventory-list> </xsl:for-each-group> </inventory> </xsl:template> </xsl:stylesheet>
XSLT 1.0 版本(兼容旧处理器)
使用Muenchian分组法实现,适配不支持XSLT 2.0的老旧处理环境。
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.demandware.com/xml/impex/inventory/2007-05-31"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 定义第一层分组key:按INVENTSITEID --> <xsl:key name="siteGroup" match="TRU_SALESFORCEONHAND" use="INVENTSITEID"/> <!-- 定义第二层分组key:按INVENTSITEID+ITEMID组合值 --> <xsl:key name="siteItemGroup" match="TRU_SALESFORCEONHAND" use="concat(INVENTSITEID, '|', ITEMID)"/> <!-- 生成当前时间戳,XSLT1.0没有内置时间函数,这里可以根据处理器扩展调整,示例用固定值可替换为实际获取逻辑 --> <xsl:variable name="currentTime" select="'2021-02-01T14:37:15.494Z'"/> <xsl:template match="/Document"> <inventory> <!-- 第一层分组:遍历所有唯一的INVENTSITEID --> <xsl:for-each select="TRU_SALESFORCEONHAND[count(. | key('siteGroup', INVENTSITEID)[1]) = 1]"> <xsl:variable name="currentSite" select="INVENTSITEID"/> <inventory-list> <header list-id="{$currentSite}"> <timestamp><xsl:value-of select="$currentTime"/></timestamp> </header> <records> <!-- 第二层分组:遍历当前站点下所有唯一的ITEMID --> <xsl:for-each select="../TRU_SALESFORCEONHAND[INVENTSITEID = $currentSite][count(. | key('siteItemGroup', concat($currentSite, '|', ITEMID))[1]) = 1]"> <xsl:variable name="currentItem" select="ITEMID"/> <record product-id="{$currentItem}"> <allocation> <xsl:value-of select="format-number(sum(key('siteItemGroup', concat($currentSite, '|', $currentItem))/ONHANDQTY), '0.000000')"/> </allocation> <allocation-timestamp><xsl:value-of select="$currentTime"/></allocation-timestamp> </record> </xsl:for-each> </records> </inventory-list> </xsl:for-each> </inventory> </xsl:template> </xsl:stylesheet>
注意事项
- XSLT 1.0本身没有内置获取当前时间的函数,可根据你使用的处理器(如Saxon、.NET XslCompiledTransform、Java Xalan)调用对应扩展函数替换
currentTime变量的固定值 - 输出命名空间已严格匹配Demandware库存接口规范,无需额外调整
- 求和逻辑默认保留6位小数,可通过修改
format-number的格式字符串调整精度
内容的提问来源于stack exchange,提问作者Tuấn Nguyễn
相关产品推荐
相关产品推荐

