You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT技术实现:为特定属性元素及后续特殊元素添加父节点

问题描述

现有如下XML结构:

<egXML>
  <measure type="Divison" n="1">
    <!-- The element with this attribute value can appear n times -->
  </measure>
  <measure type="End_Cycle" n="4">
    <!-- This element appears once after the previous elements with @type="Division" -->
  </measure>
  <measure type="Divison" n="5">
    <!-- This element with this attribute value can appear n times -->
  </measure>
  <measure type="End_Cycle" n="10">
    <!-- This element appears once after the previous elements with @type="Division"-->
  </measure>
</egXML>

需求是将若干个@type="Division"的measure元素,加上紧随其后的一个@type="End_Cycle"的measure元素组成一个主题块,用<section>标签包裹,最终输出结构如下:

<egXML>
  <section n="A">
    <measure type="Division" n="1">
    </measure>
    <measure type="Division" n="2">
    </measure>
    <!-- And as many as there are until the one with @type="End_Cycle" appears -->
    <measure type="End_Cycle" n="4">
    </measure>
  </section>
  <section n="B">
    <measure type="Division" n="5">
    </measure>
    <!-- And so on -->
    <measure type="End_Cycle" n="10">
    </measure>
  </section>
</egXML>

尝试过多种分组方式,但要么把所有@type="Division"元素放进同一个<section>,要么每个元素单独一个<section>,需要正确的XSLT实现方案。

解决方案

方案1:XSLT 2.0/3.0(推荐,语法更简洁)

利用for-each-group的group-ending-with特性,直接指定分组的结束标记是@type="End_Cycle"的measure元素,逻辑清晰:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <!-- 复制根节点 -->
  <xsl:template match="egXML">
    <egXML>
      <!-- 按End_Cycle元素分组 -->
      <xsl:for-each-group select="measure" group-ending-with="measure[@type='End_Cycle']">
        <!-- 生成section标签,n属性用字母序列A、B、C... -->
        <section n="{translate(position(), '1234567890', 'ABCDEFGHIJ')}">
          <!-- 输出当前组内的所有measure元素 -->
          <xsl:copy-of select="current-group()"/>
        </section>
      </xsl:for-each-group>
    </egXML>
  </xsl:template>
</xsl:stylesheet>

逻辑说明:

  • group-ending-with="measure[@type='End_Cycle']":表示每遇到一个@type="End_Cycle"的measure,就将它和之前所有未分组的measure(也就是@type="Division"的元素)组成一个分组。
  • current-group():获取当前分组内的所有元素,直接复制输出即可。
  • position()生成的数字转字母:用translate函数把1-10转成A-J,如果需要更多字母可以扩展映射规则。

方案2:XSLT 1.0(兼容旧版本处理器)

XSLT 1.0没有原生分组函数,需要用Muenchian分组结合轴定位来实现:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <!-- 定义键:按每个End_Cycle元素的位置,标记对应的分组 -->
  <xsl:key name="group-by-end-cycle" match="measure[@type!='End_Cycle']"
           use="generate-id(following-sibling::measure[@type='End_Cycle'][1])"/>

  <!-- 复制根节点 -->
  <xsl:template match="egXML">
    <egXML>
      <!-- 遍历所有End_Cycle元素,每个对应一个section -->
      <xsl:for-each select="measure[@type='End_Cycle']">
        <xsl:variable name="current-end" select="."/>
        <!-- 生成section的n属性,用字母序列 -->
        <section n="{translate(position(), '1234567890', 'ABCDEFGHIJ')}">
          <!-- 输出当前组内的Division元素 -->
          <xsl:copy-of select="key('group-by-end-cycle', generate-id($current-end))"/>
          <!-- 输出当前End_Cycle元素 -->
          <xsl:copy-of select="$current-end"/>
        </section>
      </xsl:for-each>
    </egXML>
  </xsl:template>
</xsl:stylesheet>

逻辑说明:

  • 用key为每个Division元素绑定它后面最近的End_Cycle元素的ID,这样所有属于同一个End_Cycle前面的Division会被归为一组。
  • 遍历每个End_Cycle元素,通过key找到对应的所有Division元素,再加上自身,组成一个<section>。

内容的提问来源于stack exchange,提问作者OttoMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:22:11