XSLT 1.0环境下基于DocEntry与LineNum实现XML多级分组咨询
XSLT 1.0 二级分组实现方案(按DocEntry+LineNum分组)
核心实现思路
- 采用XSLT 1.0原生支持的Muenchian分组法实现二级层级分组,性能优于
not(preceding::)的判断写法,适配所有支持XSLT 1.0的运行环境 - 一级分组维度:
DocEntry,用于聚合同一单据的所有行数据 - 二级分组维度:
DocEntry与LineNum拼接的组合键,用于在同一单据下按行号聚合行属性、序列号等关联数据
完整实现代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义一级分组键:按DocEntry分组所有行节点 --> <xsl:key name="docKey" match="Row" use="DocEntry"/> <!-- 定义二级分组键:按DocEntry+LineNum组合分组同一单据下的同行数据 --> <xsl:key name="lineKey" match="Row" use="concat(DocEntry, '|', LineNum)"/> <xsl:template match="/"> <xsl:for-each select="//Row[count(. | key('docKey', DocEntry)[1]) = 1]"> <object> <string name="DocEntry"> <xsl:value-of select="DocEntry"/> </string> <string name="DocDate"> <xsl:value-of select="DocDate"/> </string> <array name="transferLines"> <!-- 二级分组:取当前DocEntry下所有唯一LineNum的节点 --> <xsl:for-each select="key('docKey', DocEntry)[count(. | key('lineKey', concat(DocEntry, '|', LineNum))[1]) = 1]"> <object> <string name="LineNum"> <xsl:value-of select="LineNum"/> </string> <string name="ItemCode"> <xsl:value-of select="ItemCode"/> </string> <string name="Quantity"> <xsl:value-of select="Quantity"/> </string> <array name="SerialNumbers"> <!-- 遍历当前行下的所有序列号节点 --> <xsl:for-each select="key('lineKey', concat(DocEntry, '|', LineNum))"> <object> <xsl:if test="SerialNumber != ''"> <string name="SerialNumber"> <xsl:value-of select="SerialNumber"/> </string> </xsl:if> </object> </xsl:for-each> </array> </object> </xsl:for-each> </array> </object> </xsl:for-each> </xsl:template> </xsl:stylesheet>
关键逻辑说明
- 分组键用
|作为分隔符拼接,避免DocEntry和LineNum拼接后出现歧义,比如DocEntry为12、LineNum为3,和DocEntry为1、LineNum为23的场景不会被误识别为同一分组 - 序列号节点输出时做了空值判断,无序列号的行输出空object,有多个序列号的行输出对应数量的object,完全匹配要求的输出结构
- 所有XSLT语法均为1.0标准语法,无扩展依赖,可直接在仅支持XSLT 1.0的环境中运行
内容的提问来源于stack exchange,提问作者user8698031
相关产品推荐
相关产品推荐

