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

如何通过XSLT正确筛选修改TOKEN节点,保留其余节点结构

XSLT转换问题:为指定TOKEN添加属性并新增空TOKEN节点

输入XML文档

<?xml version="1.0" encoding="utf-8"?>
<DOCUMENT>
    <SECTION>
        <PARAGRAPH TRACK="4">
            <SENTENCE NAME="PRIMARY" COUNT="4">
                <TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
                <TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="2">
                <TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES"/>
                <TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="1">
                <TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES"/>
            </SENTENCE>
        </PARAGRAPH>
    </SECTION>
</DOCUMENT>

期望转换结果

<?xml version="1.0" encoding="utf-8"?>
<DOCUMENT>
    <SECTION>
        <PARAGRAPH TRACK="4">
            <SENTENCE NAME="PRIMARY" COUNT="4">
                <TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
                <TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="2">
                <TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES"/>
                <TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="1">
                <TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" NEW="YES"/>
                <TOKEN/>
            </SENTENCE>
        </PARAGRAPH>
    </SECTION>
</DOCUMENT>

当前错误的XSLT代码

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:key name="primary_tokens" match="SENTENCE[@NAME='PRIMARY']/TOKEN" use="concat(@SENTENCE_BEGIN,'|',@SENTENCE_END)"/>
    <xsl:template match="/*">
        <xsl:for-each select=".//TOKEN[@DOUBLE='YES'][key('primary_tokens',concat(@SENTENCE_BEGIN,'|',@SENTENCE_END))]">
            <xsl:if test="key('primary_tokens',concat(@SENTENCE_BEGIN,'|',@SENTENCE_END))[@BEGIN &gt; current()/@BEGIN]">
                <xsl:copy>
                    <xsl:attribute name="NEW">YES</xsl:attribute>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:copy>
            </xsl:if>
        </xsl:for-each>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

错误输出结果

<?xml version="1.0" encoding="utf-16"?>
<TOKEN NEW="YES" BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES"/>
<DOCUMENT>
    <SECTION>
        <PARAGRAPH TRACK="4">
            <SENTENCE NAME="PRIMARY" COUNT="4">
                <TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156"/>
                <TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
                <TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="2">
                <TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES"/>
                <TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B"/>
            </SENTENCE>
            <SENTENCE NAME="SECONDARY" COUNT="1">
                <TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES"/>
            </SENTENCE>
        </PARAGRAPH>
    </SECTION>
</DOCUMENT>

正确的XSLT解决方案

你之前的错误核心在于匹配根节点/*的模板里,先输出了选中的TOKEN,再复制整个文档,导致TOKEN被放到了文档最开头。我们基于XSLT的身份模板(默认复制所有节点),针对需要修改的节点单独编写模板即可解决问题:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" encoding="utf-8"/>
    <xsl:strip-space elements="*"/>

    <!-- 身份模板:默认复制所有节点和属性,保证大部分内容原样保留 -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- 定义键,用于匹配PRIMARY句子中同范围的TOKEN -->
    <xsl:key name="primary_tokens" 
             match="SENTENCE[@NAME='PRIMARY']/TOKEN" 
             use="concat(@SENTENCE_BEGIN,'|',@SENTENCE_END)"/>

    <!-- 为符合条件的TOKEN添加NEW="YES"属性 -->
    <xsl:template match="TOKEN[@DOUBLE='YES']
                         [key('primary_tokens', concat(@SENTENCE_BEGIN,'|',@SENTENCE_END))
                          [@BEGIN > current()/@BEGIN]]">
        <xsl:copy>
            <!-- 先添加NEW属性 -->
            <xsl:attribute name="NEW">YES</xsl:attribute>
            <!-- 再复制原有的所有属性 -->
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <!-- 为指定的SECONDARY句子添加空TOKEN节点 -->
    <xsl:template match="SENTENCE[@NAME='SECONDARY' and @COUNT='1']">
        <xsl:copy>
            <!-- 复制句子的所有属性 -->
            <xsl:apply-templates select="@*"/>
            <!-- 复制句子下的所有子节点(即符合条件的TOKEN) -->
            <xsl:apply-templates select="node()"/>
            <!-- 添加新的空TOKEN节点 -->
            <TOKEN/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

方案说明:

  1. 身份模板:保留默认的节点复制行为,确保无需修改的节点直接原样输出。
  2. TOKEN匹配模板:精准定位符合条件的TOKEN(@DOUBLE='YES'且同范围的PRIMARY句子中有BEGIN值更大的TOKEN),在原位置复制时新增NEW="YES"属性,不会打乱节点顺序。
  3. SENTENCE匹配模板:针对NAME='SECONDARY'且COUNT='1'的句子,复制完原有子节点后,追加一个空的<TOKEN/>节点,满足需求。

运行这个XSLT就能得到你期望的转换结果了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:34