XSLT相邻同class元素分组如何保留其他节点原有位置
问题根因
原有代码存在两个核心问题:
- 匹配
<body>的模板中,xsl:for-each-group仅将aside元素作为分组选择对象,<p>、<img>等非aside子节点完全未被纳入处理流程,输出时直接丢失。 - 遍历分组内容时使用
current-group()/*仅选择<aside>的子元素,会丢失<aside>标签下直接存在的文本节点;同时未保留<body>根标签本身,不符合输出结构要求。
修复方案
基于XSLT 2.0+的for-each-group能力,调整分组逻辑覆盖<body>下所有子节点,通过分组键区分需要合并的相邻同class<aside>和需要保留的独立非aside节点即可,完整可运行代码如下:
<!-- 身份模板,原样拷贝所有未单独匹配的节点和属性 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="body"> <xsl:copy> <!-- 选中body下所有直接子元素做分组,而非仅选aside --> <xsl:for-each-group select="*" group-adjacent=" if (self::aside) then concat('aside_', @class) else generate-id() "> <!-- 判断当前组是否是aside分组 --> <xsl:choose> <xsl:when test="self::aside"> <!-- 按class映射对应的包裹标签名 --> <xsl:variable name="grouping_tag"> <xsl:choose> <xsl:when test="@class = '1'">tag1</xsl:when> <xsl:when test="@class = '2'">tag2</xsl:when> <xsl:otherwise>tagX</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:element name="{$grouping_tag}"> <xsl:attribute name="class" select="@class"/> <!-- 遍历组内所有aside,拷贝其全部内容(含文本、子标签) --> <xsl:for-each select="current-group()"> <xsl:apply-templates select="node()"/> </xsl:for-each> </xsl:element> </xsl:when> <!-- 非aside节点,直接原样输出 --> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:copy> </xsl:template>
关键修改点说明
- 分组范围调整为
<body>下所有直接子元素(select="*"),从根源上避免非aside节点被遗漏 - 分组键逻辑做了分支判断:相邻同class的
<aside>使用相同分组键自动合并,非aside节点每个生成唯一分组键,保证独立存在不被合并 - 增加
<body>标签本身的拷贝逻辑,符合输出结构要求 - 修正原代码中仅遍历
<aside>子元素的问题,直接处理<aside>下所有子节点(含直接文本),避免内容丢失 - 非aside分组直接走身份模板拷贝,完全保留原有结构、属性和位置
内容的提问来源于stack exchange,提问作者CDM95
相关产品推荐
相关产品推荐

