如何使用XSLT 1.0实现节点值去重后按分隔符拼接输出
XSLT 1.0 实现SAP IDOC VBELN字段去重拼接方案
需求背景
- 转换目标:将SAP IDOC XML转换为外部系统要求的XML格式,提取源节点
E1EDT20下所有E1EDL20子节点的VBELN字段值,剔除重复值后用/分隔拼接,填充到目标字段REFERENCE1 - 现存问题:XSLT 3.0环境下可直接用
distinct-values()+string-join()实现需求,但Azure平台仅支持XSLT 1.0,常规遍历写法无法自动去重,会输出重复值(例:输出字段中重复出现0300166930)
实现方案
XSLT 1.0没有内置去重函数,采用工业界通用的Muenchian分组法实现,性能和兼容性均满足Azure平台要求,完整代码如下:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义索引键:按VBELN的值对所有目标节点建立索引 --> <xsl:key name="kVBELN" match="E1EDT20/E1EDL20/VBELN" use="."/> <xsl:template match="/"> <!-- 此处替换为你实际的目标XML根结构 --> <TargetXML> <REFERENCE1> <!-- 筛选所有不重复的VBELN节点:仅保留同值节点组的第一个节点 --> <xsl:for-each select="E1EDT20/E1EDL20/VBELN[count(. | key('kVBELN', .)[1]) = 1]"> <xsl:sort select="."/> <!-- 不需要排序可删除此行 --> <xsl:value-of select="."/> <!-- 非最后一个节点时追加分隔符,避免末尾出现多余斜杠 --> <xsl:if test="position() != last()"> <xsl:text>/</xsl:text> </xsl:if> </xsl:for-each> </REFERENCE1> <!-- 此处补充其余目标字段的转换逻辑 --> </TargetXML> </xsl:template> </xsl:stylesheet>
关键说明
- 性能优势:
xsl:key会预先给目标节点建立值索引,去重过程不需要全量遍历节点对比,节点量较大时性能远高于手写逐值判断的遍历逻辑 - 去重逻辑:筛选条件
count(. | key('kVBELN', .)[1]) = 1是Muenchian分组的标准写法,仅保留每个重复值组里的第一个节点,自动过滤所有重复项 - 命名空间适配:SAP原生IDOC通常带默认命名空间(如
xmlns="urn:sap-com:document:sap:idoc:messages"),需要在样式表中声明命名空间前缀,且所有节点选择路径都加上前缀,否则会匹配不到节点,示例写法如下:
<!-- 适配SAP IDOC默认命名空间的样式表头 --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="urn:sap-com:document:sap:idoc:messages" exclude-result-prefixes="sap"> <!-- 键定义和节点选择时都加上sap:前缀,例如match="sap:E1EDT20/sap:E1EDL20/sap:VBELN" --> </xsl:stylesheet>
注意:不要采用“遍历每个节点时检查前面是否存在同值节点”的去重写法,该写法时间复杂度为O(n²),IDOC节点较多时转换速度会非常慢。
内容的提问来源于stack exchange,提问作者PeterS
相关产品推荐
相关产品推荐

