基于XSLT的节点交叉评估:为TOKEN节点添加指定属性
搞定SECONDARY句子TOKEN的FURTHER_FROM_PRIMARY属性添加问题
嘿,我来帮你解决这个XML转换的需求!核心目标是:在同一个<PARAGRAPH>节点下,给那些BEGIN属性值比同组其他所有<SENTENCE type="SECONDARY">里的<TOKEN>都大的节点,加上FURTHER_FROM_PRIMARY="YES"属性对吧?下面是完整的解决方案,包括思路、代码和解释:
核心思路拆解
- 锁定上下文:只在当前
<PARAGRAPH>范围内比较,不能跨段落乱比,不然结果肯定出错。 - 数值化比较:
BEGIN属性是字符串格式的数字,必须转成数值再对比,不然会出现"100"比"99"小的字符串排序坑。 - 精准判断:对每个目标TOKEN,检查它的
BEGIN是不是同组里的最大值——是就加属性,不是就原样保留节点结构。
完整XSLT实现
<?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> <!-- 针对PARAGRAPH下SECONDARY句子中TOKEN的特殊处理逻辑 --> <xsl:template match="PARAGRAPH/SENTENCE[@type='SECONDARY']/TOKEN"> <!-- 将当前TOKEN的BEGIN转为数值,避免字符串排序错误 --> <xsl:variable name="currentBegin" select="number(@BEGIN)"/> <!-- 收集当前PARAGRAPH下所有SECONDARY句子中TOKEN的BEGIN数值 --> <xsl:variable name="allSecondaryTokenBegins" select="number(../../SENTENCE[@type='SECONDARY']/TOKEN/@BEGIN)"/> <xsl:copy> <!-- 复制原有的所有属性 --> <xsl:apply-templates select="@*"/> <!-- 判断条件:当前BEGIN是同组最大值,且同组有多个TOKEN(避免单个TOKEN误加属性) --> <xsl:if test="$currentBegin > max($allSecondaryTokenBegins) and count($allSecondaryTokenBegins) > 1"> <xsl:attribute name="FURTHER_FROM_PRIMARY">YES</xsl:attribute> </xsl:if> <!-- 复制TOKEN的子节点(如果存在) --> <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
关键部分解释
- 默认模板:确保所有未被特殊模板匹配的节点和属性都被原样复制,完美保留XML原有结构。
- 变量
currentBegin:把当前TOKEN的BEGIN属性转成数值,这是避免字符串排序错误的关键。 - 变量
allSecondaryTokenBegins:严格限定在当前<PARAGRAPH>下收集所有SECONDARY句子的TOKEN的BEGIN值,保证比较范围正确。 - 条件判断:
$currentBegin > max($allSecondaryTokenBegins):检查当前TOKEN的BEGIN是否是同组中的最大值。count($allSecondaryTokenBegins) > 1:避免当只有一个SECONDARY TOKEN时误加属性(如果你的需求允许单个TOKEN也添加属性,可以直接去掉这个条件)。
测试示例(输入→输出)
输入XML片段
<PARAGRAPH> <SENTENCE type="PRIMARY"> <TOKEN BEGIN="50" END="55">Hello</TOKEN> </SENTENCE> <SENTENCE type="SECONDARY"> <TOKEN BEGIN="100" END="105">word1</TOKEN> <TOKEN BEGIN="200" END="206">word2</TOKEN> <TOKEN BEGIN="150" END="153">word3</TOKEN> </SENTENCE> <SENTENCE type="SECONDARY"> <TOKEN BEGIN="250" END="254">word4</TOKEN> </SENTENCE> </PARAGRAPH>
转换后输出XML片段
<PARAGRAPH> <SENTENCE type="PRIMARY"> <TOKEN BEGIN="50" END="55">Hello</TOKEN> </SENTENCE> <SENTENCE type="SECONDARY"> <TOKEN BEGIN="100" END="105">word1</TOKEN> <TOKEN BEGIN="200" END="206" FURTHER_FROM_PRIMARY="YES">word2</TOKEN> <TOKEN BEGIN="150" END="153">word3</TOKEN> </SENTENCE> <SENTENCE type="SECONDARY"> <TOKEN BEGIN="250" END="254">word4</TOKEN> </SENTENCE> </PARAGRAPH>
可以看到,BEGIN="200"的TOKEN被正确添加了目标属性,而单个SECONDARY TOKEN的word4没有添加(符合我们的条件判断逻辑)。
常见错误排查
如果你之前的代码没生效,大概率是这几个原因:
- 没有限定PARAGRAPH上下文:比如直接取所有SECONDARY的TOKEN,导致跨段落比较,结果完全错误。
- 用了字符串比较:直接对比
@BEGIN而不是转成number(@BEGIN),出现数值排序错误(比如"100" < "99")。 - 没处理边界情况:比如单个SECONDARY TOKEN的场景,导致不必要的属性添加。
内容的提问来源于stack exchange,提问作者wild card
相关产品推荐
相关产品推荐

