XSLT:不改变节点顺序的表格分组与合并问题
XSLT 2.0 实现TABLE按TABLETHEAD文本分组并保留兄弟节点顺序
我明白你遇到的痛点——之前用Muenchian分组时,因为TABLE和IMAGE、GRAPHIC等节点同级交错,要么丢了兄弟节点,要么打乱了顺序。其实XSLT 2.0的for-each-group指令刚好能解决这种混合节点的分组需求,完美兼顾分组逻辑和原始节点的顺序、内容保留。
核心思路
我们要把父节点下的所有子节点分成两类组处理:
- TABLE节点组:按
TABLETHEAD的规范化文本内容分组,同一文本的TABLE合并成一个,保留一个表头、合并所有行 - 非TABLE节点组:每个节点单独成组,保证它们的原始顺序和内容完全不变
完整XSLT示例
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 匹配根节点,传递到父节点处理 --> <xsl:template match="/"> <xsl:apply-templates select="*"/> </xsl:template> <!-- 匹配包含TABLE和其他兄弟节点的父节点(替换成你实际的父节点名称,比如DOC/CONTENT) --> <xsl:template match="*[node-name()='CONTENT']"> <xsl:copy> <!-- 对所有子节点分组:TABLE按TABLETHEAD文本分组,其他节点单独成组 --> <xsl:for-each-group select="node()" group-by=" if (self::TABLE) then normalize-space(string(TABLETHEAD)) else generate-id() "> <!-- 处理TABLE分组:合并成单个TABLE --> <xsl:if test="current-group()[self::TABLE]"> <TABLE> <!-- 保留分组内第一个TABLE的TABLETHEAD(含所有子结构和属性) --> <xsl:copy-of select="current-group()[1]/TABLETHEAD"/> <!-- 合并分组内所有TABLE的TABLEROW --> <xsl:copy-of select="current-group()/TABLEROW"/> </TABLE> </xsl:if> <!-- 处理非TABLE分组:直接输出所有节点(包括文本、注释、空白节点) --> <xsl:if test="not(current-group()[self::TABLE])"> <xsl:copy-of select="current-group()"/> </xsl:if> </xsl:for-each-group> </xsl:copy> </xsl:template> <!-- 身份模板:确保未匹配的节点/属性原样保留 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
关键细节说明
分组键的设计:
- 对TABLE节点,用
normalize-space(string(TABLETHEAD))作为分组键:normalize-space去除多余空格,string()确保即使TABLETHEAD包含子元素(比如<b>、<span>),也能获取完整文本内容,避免因格式差异导致分组错误。 - 对非TABLE节点,用
generate-id()作为分组键:每个节点生成唯一ID,保证每个非TABLE节点都是单独一组,不会被合并,完美保留原始顺序。
- 对TABLE节点,用
TABLE合并逻辑:
- 只保留分组内第一个TABLE的TABLETHEAD,避免重复表头
- 把分组内所有TABLE的TABLEROW全部复制到新TABLE下,实现行合并
灵活调整:
- 请把示例中的
*[node-name()='CONTENT']替换成你实际的父节点名称(比如你的XML中TABLE的直接父节点是<DOC>就写成match="DOC") - 如果TABLETHEAD有特殊属性或子结构,
copy-of会自动保留,无需额外处理
- 请把示例中的
为什么这个方案能解决你的问题?
和Muenchian分组不同,for-each-group可以直接遍历所有类型的节点,不需要提前筛选TABLE,这样就能在分组的同时,自动保留所有非TABLE兄弟节点的位置和内容,完全不会打乱原始XML的节点顺序。
内容的提问来源于stack exchange,提问作者LHark
相关产品推荐
相关产品推荐

