You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 05:33:01