XSLT 2.0按ID属性合并节点 将J开头ID节点归入新父节点
XSLT 2.0 按ID前缀分组合并节点实现方案
问题根因
- 原代码模板匹配路径错误:源XML中Row节点的父容器是
RepData,原代码写为匹配/Rep/Report,无法选中目标节点 - 原分组逻辑直接以完整ID值为分组键,没有先按「ID是否以J开头」做第一层分桶,无法将J开头的节点归集到同一父节点下
- 原代码拼接层级id时直接用逗号连接值,不符合父路径逐层拼接的要求
修正后完整XSLT代码
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 修正匹配路径,命中正确的RepData节点 --> <xsl:template match="/Rep/RepData"> <ccs> <!-- 第一层分组:J开头ID归为一组,其余ID各自保留独立分组逻辑 --> <xsl:for-each-group select="Row" group-by="starts-with(Field[@name='ID'], 'J')"> <xsl:choose> <!-- 处理J开头ID分组,生成统一父ccwc节点 --> <xsl:when test="current-grouping-key() = true()"> <ccwc id="1-J"> <name>One J</name> <cccs> <!-- 组内按原规则按ID做第二层分组 --> <xsl:for-each-group select="current-group()" group-by="Field[@name='ID']"> <xsl:variable name="ref" select="current-grouping-key()"/> <ccwc id="{concat('1-J/', $ref)}"> <name> <xsl:value-of select="if (string-length(Field[@name='CName']) != 0) then Field[@name='CName'] else 'Unknown'"/> </name> <cccs> <!-- 第三层按GPL分组,保持原有ccc节点生成规则 --> <xsl:for-each-group select="current-group()" group-by="Field[@name='GPL']"> <xsl:variable name="childRef" select="current-grouping-key()"/> <ccc id="{concat('1-J/', $ref, '/', $childRef)}"> <name><xsl:value-of select="$childRef"/></name> </ccc> </xsl:for-each-group> </cccs> </ccwc> </xsl:for-each-group> </cccs> </ccwc> </xsl:when> <!-- 处理非J开头ID,完全保留原有输出结构 --> <xsl:otherwise> <xsl:for-each-group select="current-group()" group-by="Field[@name='ID']"> <xsl:variable name="ref" select="current-grouping-key()"/> <ccwc id="{$ref}"> <name> <xsl:value-of select="if (string-length(Field[@name='CName']) != 0) then Field[@name='CName'] else 'Unknown'"/> </name> <cccs> <xsl:for-each-group select="current-group()" group-by="Field[@name='GPL']"> <xsl:variable name="childRef" select="current-grouping-key()"/> <ccc id="{concat($ref, '/', $childRef)}"> <name><xsl:value-of select="$childRef"/></name> </ccc> </xsl:for-each-group> </cccs> </ccwc> </xsl:for-each-group> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </ccs> </xsl:template> </xsl:transform>
关键调整点说明
- 修正模板匹配路径:将原错误的
/Rep/Report改为源XML实际存在的/Rep/RepData,保证模板能正确命中目标节点 - 新增第一层分组逻辑:以
starts-with(Field[@name='ID'], 'J')作为分组键,所有J开头的Row会被归集到同一分组,非J开头的Row归集到另一分组 - 针对J开头的分组,单独生成固定属性的父级ccwc节点:id固定为
1-J,name固定为One J;组内所有J开头的Row沿用原有的ID、GPL两层分组规则生成子节点,所有子节点的id从父节点id开始逐层拼接 - 非J开头的分组内,完全沿用原有的按ID、GPL两层分组逻辑,输出结构和规则保持一致,id按自身路径拼接
- 用XSLT 2.0原生支持的属性值模板(AVT)、if表达式简化冗余的变量定义和条件判断,提升代码可读性和执行效率
输出结构说明
转换后根节点<ccs>下共生成3个平级的一级ccwc节点:
- id为
A的ccwc节点,对应ID=A的非J类Row,内部结构和原规则一致 - id为
1-J的ccwc节点,其<cccs>子节点下挂载J-A、J-U、J-I三个二级ccwc节点,每个二级ccwc下再按GPL规则挂载对应的ccc节点,所有节点id从父级开始逐层拼接 - id为
M的ccwc节点,对应ID=M的非J类Row,内部结构和原规则一致
如果需要调整id拼接的分隔符,直接修改
concat()函数中的连接参数即可。
内容的提问来源于stack exchange,提问作者user13583928
相关产品推荐
相关产品推荐

