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

XSL技术疑问:如何选择节点到根的所有祖先,排除后续兄弟节点

问题描述

我有如下XML payload:

<Root>

    <Header>
        <Name>B</Name>
    </Header>
    <Item>
        <Id>10</Id>
        <Description>Item10</Description>
    </Item>
    <Item>
        <Id>20</Id>
        <Description>Item20</Description>
    </Item>
    <Package>
        <Id>A</Id>
    </Package>
    <Package>
        <Id>B</Id>
    </Package>
    <Package>
        <Id>C</Id>
    </Package>
</Root>

我希望针对每个Package节点,输出其从自身到根节点的所有祖先组成的完整消息,但不包含当前Package的后续兄弟节点(其他Package),期望输出为三个分别对应单个Package的Root结构:

<Root>

    <Header>
        <Name>B</Name>
    </Header>
    <Item>
        <Id>10</Id>
        <Description>Item10</Description>
    </Item>
    <Item>
        <Id>20</Id>
        <Description>Item20</Description>
    </Item>
    <Package>
        <Id>A</Id>
    </Package>

</Root>
<Root>

    <Header>
        <Name>B</Name>
    </Header>
    <Item>
        <Id>10</Id>
        <Description>Item10</Description>
    </Item>
    <Item>
        <Id>20</Id>
        <Description>Item20</Description>
    </Item>

    <Package>
        <Id>B</Id>
    </Package>

</Root>
<Root>

    <Header>
        <Name>B</Name>
    </Header>
    <Item>
        <Id>10</Id>
        <Description>Item10</Description>
    </Item>
    <Item>
        <Id>20</Id>
        <Description>Item20</Description>
    </Item>

    <Package>
        <Id>C</Id>
    </Package>
</Root>

但目前我的代码输出的3条消息均包含所有Package,我的代码如下:

<?xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"?>

<xsl:template match="/">
<Messages>
        <xsl:for-each select="//Package">
            <xsl:copy-of select="/*[not(following-sibling::*)]" />
        </xsl:for-each>
    </Messages>
</xsl:template>

请问我的代码哪里出错了?


错误分析

你的代码存在两个核心问题:

  1. 逻辑完全偏离预期:<xsl:copy-of select="/*[not(following-sibling::*)]" /> 里的/*直接选中了整个根节点<Root>,而[not(following-sibling::*)]的判断永远为真(XML只能有一个根节点,自然没有后续兄弟),所以每次循环都原样复制了完整的原始XML,导致每个输出都包含所有Package节点。
  2. 未区分当前目标Package:你没有针对循环中的当前Package节点,做任何过滤其后续兄弟节点的操作,完全没实现“只保留当前Package”的要求。

修正后的XSLT代码

方案一(通用场景适配)

适合后续XML结构可能变化的情况,自动保留当前Package之前的所有非Package节点,以及当前Package:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

    <xsl:template match="/Root">
        <xsl:for-each select="Package">
            <Root>
                <!-- 复制Root下所有非Package节点,以及当前Package之前的兄弟节点(如果有) -->
                <xsl:apply-templates select="../*[not(self::Package) or generate-id() = generate-id(current())]"/>
            </Root>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

方案二(针对当前场景简化)

如果XML结构固定,非Package节点只有Header和Item,可以直接指定复制目标,写法更直观:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
        <xsl:for-each select="//Package">
            <Root>
                <!-- 复制所有Header和Item节点 -->
                <xsl:copy-of select="/Root/Header | /Root/Item"/>
                <!-- 复制当前循环的Package节点 -->
                <xsl:copy-of select="."/>
            </Root>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:12:56