XSLT问题:仅当属性值不存在时添加节点异常排查
XSLT模板错误排查:错误为所有带placeatentry的item添加entry节点
问题背景
处理给定XML文件时,需求是仅当全局不存在值与placeatentry属性匹配的<entry>节点时,为带有placeatentry属性的<item>添加<entrycl/entry>节点,并为<entrycl>添加new="yes"属性,预期仅blue对应的item会新增节点,但当前编写的XSLT会给所有带placeatentry的item都添加节点。
输入XML
<?xml version="1.0" encoding="UTF-8"?> <items> <item sort="gruun"> <entrycl> <entry>gruun</entry> <gramt>bn</gramt></entrycl> <translcl> <transl>green</transl> </translcl> </item> <item> <entrycl placeatentry="gruun"> <entryx>it is grün</entryx> </entrycl> <translcl><transl>bla bla bla</transl> </translcl> </item> <item> <entrycl placeatentry="blue"> <entryx>it is blue</entryx></entrycl> <translcl> <transl>blu blu blu</transl> </translcl> </item> </items>
需求说明
- 仅当全局不存在值等于
placeatentry属性值的<entry>节点时,为带有placeatentry属性的<item>添加<entrycl/entry>节点(值为placeatentry属性值) - 同时为该
<entrycl>添加new="yes"属性 - 预期仅
blue对应的<item>会新增节点
预期输出
<items> <item sort="gruun"> <entrycl> <entry>gruun</entry> <gramt>bn</gramt></entrycl> <translcl> <transl>ble ble ble</transl> <rem>existing item</rem> </translcl> </item> <item> <entrycl placeatentry="gruun"> <entryx>it is grün</entryx> </entrycl> <translcl><transl>bla bla bla</transl> </translcl> </item> <item> <entrycl new="yes"> <entry>blue</entry> <entryx>it is blue</entryx></entrycl> <translcl> <transl>blu blu blu</transl> </translcl> </item> </items>
用户编写的XSLT模板
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="item[entrycl/@placeatentry and not(//entry[.=entrycl/@placeatentry])]"> <xsl:copy> <entrycl> <entry new="yes"> <xsl:value-of select="entrycl/@placeatentry"/> </entry> <xsl:apply-templates select="entrycl/node()"/> </entrycl> <xsl:apply-templates select="@*|node()[not(self::entrycl)]"/> </xsl:copy> <!-- </xsl:if> --> </xsl:template> </xsl:stylesheet>
问题
执行该XSLT后,所有带placeatentry属性的<item>都被添加了新的<entry>节点,不符合预期,请问代码哪里出错了?
错误原因与修正
错误点分析
你的模板匹配条件not(//entry[.=entrycl/@placeatentry])犯了上下文混淆的错误:在XPath的谓词(方括号内的判断逻辑)中,entrycl/@placeatentry的上下文是当前遍历到的<entry>节点,而非外层的<item>节点。但<entry>节点根本没有<entrycl>父节点,所以entrycl/@placeatentry会返回空值,导致//entry[.=空值]永远不成立,not(...)就永远为真,所有带placeatentry的<item>都会被这个模板处理。
另外你原代码还把new="yes"加在了<entry>节点上,而预期输出里是要加到<entrycl>节点上,这也不符合需求。
修正方案
通过变量保存当前<item>的placeatentry值,明确上下文;同时拆分逻辑,分情况处理是否需要新增节点。
修正后的XSLT模板
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="item[entrycl/@placeatentry]"> <!-- 保存当前item的placeatentry属性值,避免上下文混淆 --> <xsl:variable name="target-value" select="entrycl/@placeatentry"/> <xsl:copy> <xsl:choose> <!-- 判断全局是否存在值匹配的entry节点 --> <xsl:when test="not(//entry[. = $target-value])"> <!-- 新增entry节点:创建带new属性的entrycl,插入entry后复制原节点内容 --> <entrycl new="yes"> <entry><xsl:value-of select="$target-value"/></entry> <xsl:apply-templates select="entrycl/node()"/> </entrycl> <xsl:apply-templates select="@*|node()[not(self::entrycl)]"/> </xsl:when> <xsl:otherwise> <!-- 无需新增:直接复制原内容 --> <xsl:apply-templates select="@*|node()"/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> </xsl:stylesheet>
修正说明
- 用变量
$target-value存储当前<item>的placeatentry值,确保后续判断时上下文不会混乱 - 通过
<xsl:choose>分支处理两种场景:不存在对应<entry>时新增节点,否则直接保留原内容 - 将
new="yes"添加到<entrycl>节点上,与预期输出一致 - 保留了原有的身份模板(匹配
@*|node()),确保其他节点和属性正常复制
内容的提问来源于stack exchange,提问作者Robert E
相关产品推荐
相关产品推荐

