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

如何用XSLT递归移除XML Schema中的冗余嵌套父元素?

移除XML Schema冗余嵌套结构的XSLT递归处理问题

背景

我通过XML Schema生成器生成系统输出XML对应的Schema,再基于该Schema生成XML读写Java代码,同时会手动修改Schema(比如添加枚举)来优化生成的代码。当系统更新导致XML格式变化时,需要重新生成Schema,并用Beyond Compare对比旧版手动优化的Schema,迁移自定义修改以避免被覆盖。

当前生成的Schema存在冗余嵌套结构:每个complexType的直接子元素为sequence,但部分sequence中包含多余的choice或sequence子元素,这不符合我对简洁、宽松Schema的需求。我编写了XSLT尝试移除这些冗余嵌套,但未完全生效,尤其是递归处理的问题。

当前XSLT代码

<xsl:template match="choice/sequence">
  <xsl:for-each select="*">
    <xsl:copy>
      <xsl:variable name="att" as="attribute() *">
        <xsl:attribute name="minOccurs">0</xsl:attribute>
      </xsl:variable>
      <xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
        <xsl:sort select="local-name()"/>
      </xsl:apply-templates>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>

<xsl:template match="sequence/choice[not(@*)]">
  <xsl:for-each select="*">
    <xsl:copy>
      <xsl:variable name="att" as="attribute() *">
        <xsl:attribute name="minOccurs">0</xsl:attribute>
      </xsl:variable>
      <xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
        <xsl:sort select="local-name()"/>
      </xsl:apply-templates>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>

<xsl:template match="sequence/sequence[@minOccurs eq '0']">
  <xsl:for-each select="*">
    <xsl:copy>
      <xsl:variable name="att" as="attribute() *">
        <xsl:attribute name="minOccurs">0</xsl:attribute>
      </xsl:variable>
      <xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
        <xsl:sort select="local-name()"/>
      </xsl:apply-templates>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>

该XSLT旨在移除choice/sequence、无属性的sequence/choice、minOccurs="0"的sequence/sequence这类冗余父元素,将其子元素提升为直接子元素,并设置minOccurs="0"以保证Schema验证逻辑一致。

问题场景

处理以下嵌套结构时:

<xs:element name="blah1">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="blah2"/>
      <xs:choice>
        <xs:element ref="blah3"/>
        <xs:sequence>
          <xs:element ref="blah4"/>
          <xs:element ref="blah5"/>
        </xs:sequence>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>
</xs:element>

得到的结果是:

<xs:element name="blah1">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="blah2"/>
      <xs:element minOccurs="0" ref="blah3"/>
      <xs:sequence minOccurs="0">
        <xs:element ref="blah4"/>
        <xs:element ref="blah5"/>
      </xs:sequence>
    </xs:sequence>
  </xs:complexType>
</xs:element>

虽然成功移除了choice元素,但内部的sequence未被递归处理,这正是需要解决的问题。

解决方案

问题根源在于当前模板仅处理直接匹配的层级,未对提升后的子元素再次触发模板匹配。要实现递归处理,需调整XSLT逻辑:不再直接复制子元素,而是对这些子元素应用模板,让XSLT处理链能继续匹配后续的冗余结构。

修改后的XSLT代码如下:

<!-- 保留默认复制模板,确保未匹配的节点正常复制 -->
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<!-- 处理choice/sequence:移除父sequence,提升子元素并设置minOccurs="0" -->
<xsl:template match="choice/sequence">
  <xsl:apply-templates select="*" mode="promote-with-minOccurs-0"/>
</xsl:template>

<!-- 处理无属性的sequence/choice:移除父choice,提升子元素并设置minOccurs="0" -->
<xsl:template match="sequence/choice[not(@*)]">
  <xsl:apply-templates select="*" mode="promote-with-minOccurs-0"/>
</xsl:template>

<!-- 处理minOccurs="0"的sequence/sequence:移除父sequence,提升子元素并设置minOccurs="0" -->
<xsl:template match="sequence/sequence[@minOccurs eq '0']">
  <xsl:apply-templates select="*" mode="promote-with-minOccurs-0"/>
</xsl:template>

<!-- 自定义模式:提升元素并设置minOccurs="0"(保留原有其他属性) -->
<xsl:template match="*" mode="promote-with-minOccurs-0">
  <xsl:copy>
    <!-- 合并原有属性(排除minOccurs)和新的minOccurs="0" -->
    <xsl:apply-templates select="@*[not(local-name() eq 'minOccurs')]">
      <xsl:sort select="local-name()"/>
    </xsl:apply-templates>
    <xsl:attribute name="minOccurs">0</xsl:attribute>
    <!-- 对当前元素的子节点应用模板,触发递归处理 -->
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

修改说明

  1. 添加默认复制模板:确保所有未被自定义模板匹配的节点都能正常复制,维持Schema的其他结构不变。
  2. 使用模式(mode)分离逻辑:创建promote-with-minOccurs-0模式,专门处理元素提升和属性设置,避免与默认模板冲突。
  3. 递归触发模板匹配:在处理冗余父元素时,调用<xsl:apply-templates>对子元素应用模板,这样提升后的子元素如果符合其他冗余结构的匹配条件(比如内部的sequence),会被对应的模板再次处理,实现递归移除。

用修改后的XSLT处理示例结构,会得到如下结果:

<xs:element name="blah1">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="blah2"/>
      <xs:element minOccurs="0" ref="blah3"/>
      <xs:element minOccurs="0" ref="blah4"/>
      <xs:element minOccurs="0" ref="blah5"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

这样就彻底移除了所有冗余的嵌套结构,同时保证了Schema的验证逻辑一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:49:55