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

基于XSLT移除缺失工厂引用的产品节点

如何用XSLT移除关联已关闭工厂的产品节点

我之前借助Stack Overflow实现过基于明确条件的XSLT过滤、移除与复制功能,但现在碰到了需要全局验证关联关系的场景,下面是抽象后的示例,想请教实现思路:

示例场景

我有数百个工厂、数千种产品,原始XML结构如下:

<data ID="1">
    <type>factory.tires</type>
    <Country>a</Country>
</data>

<data ID="2">
    <type>factory.glass</type>
    <Country>a</Country>
</data>

<data ID="3">
    <type>factory.steel</type>
    <Country>b</Country>
</data>

<data ID="4">
    <type>factory.tires</type>
    <Country>c</Country>
</data>

<!-- xxxxxxxxxxxxxx -->

<data ID="11">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="12">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="13">
    <type>product.cups</type>
    <factory REF="2" />
</data>

<data ID="20">
    <type>product.wheel</type>
    <factory REF="1" />
</data>

<data ID="21">
    <type>product.wheel</type>
    <factory REF="4" />
</data>

现在工厂1和2已关闭,对应<data>节点已被移除,当前XML如下:

<data ID="3">
    <type>factory.steel</type>
    <Country>b</Country>
</data>

<data ID="4">
    <type>factory.tires</type>
    <Country>c</Country>
</data>

<!-- xxxxxxxxxxxxxx -->

<data ID="11">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="12">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="13">
    <type>product.cups</type>
    <factory REF="2" />
</data>

<data ID="20">
    <type>product.wheel</type>
    <factory REF="1" />
</data>

<data ID="21">
    <type>product.wheel</type>
    <factory REF="4" />
</data>

需求

需要移除所有关联已关闭工厂的产品节点,但无法提前知道哪些工厂已关闭。具体逻辑是:

  • 找出所有包含<factory>子节点的<data>节点(即产品节点)
  • 检查该节点<factory>的REF属性值,是否存在于当前XML中某个<data>节点的ID属性里
  • 如果找不到对应的ID(说明关联的工厂已被移除),则删除该产品节点

预期结果是移除ID为13和20的节点,最终XML如下:

<data ID="3">
    <type>factory.steel</type>
    <Country>b</Country>
</data>

<data ID="4">
    <type>factory.tires</type>
    <Country>c</Country>
</data>

<!-- xxxxxxxxxxxxxx -->

<data ID="11">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="12">
    <type>product.screws</type>
    <factory REF="3" />
</data>

<data ID="21">
    <type>product.wheel</type>
    <factory REF="4" />
</data>

解决方案

XSLT 1.0 实现

XSLT 1.0可以通过**键(key)**快速缓存存在的工厂ID,避免重复遍历,适合大数据量场景:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 定义键:匹配所有存在的data节点,用ID作为键值 -->
    <xsl:key name="existing-factories" match="data" use="@ID" />

    <!-- 身份模板:默认复制所有节点 -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <!-- 匹配包含factory子节点的产品节点 -->
    <xsl:template match="data[factory]">
        <!-- 检查REF对应的ID是否存在,存在则保留节点 -->
        <xsl:if test="key('existing-factories', factory/@REF)">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()" />
            </xsl:copy>
        </xsl:if>
        <!-- 不存在则不输出,即移除节点 -->
    </xsl:template>
</xsl:stylesheet>

XSLT 2.0/3.0 实现

XSLT 2.0及以上支持更简洁的语法,利用exists()直接验证关联关系:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 浅复制模式:替代传统身份模板,简化代码 -->
    <xsl:mode on-no-match="shallow-copy" />

    <!-- 匹配并移除关联不存在工厂的产品节点 -->
    <xsl:template match="data[factory][not(exists(/data[@ID = current()/factory/@REF]))]" />
</xsl:stylesheet>

说明

  • 键的作用是缓存所有存在的<data>节点ID,查询效率更高,适配你提到的数千产品的场景
  • XSLT 3.0的shallow-copy模式可以大幅简化模板代码,减少冗余
  • 核心逻辑都是验证factory/@REF对应的data[@ID]是否存在,不存在则过滤该产品节点

内容的提问来源于stack exchange,提问作者Pierre de la Verre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:29:55