XSLT中元素过滤与元素值重命名同步骤实现问题咨询
听起来你遇到的是XSLT模板协作的典型小坑——单独跑都正常的逻辑,凑一起就掉链子,大概率是模板的执行顺序、上下文处理或者节点复制方式出了问题。我来帮你拆解可能的原因和解决办法:
1. 最常见的坑:xsl:copy-of vs xsl:apply-templates
如果你的过滤模板里用了<xsl:copy-of select="."/>来保留符合条件的<product>,那这就是问题根源!xsl:copy-of会直接原样复制整个节点树,完全不会触发任何针对子节点(比如<title>)的模板匹配。而你单独测试重命名模板时,是直接对整个文档应用模板,所以<title>能被正常匹配到。
解决办法:把xsl:copy-of换成xsl:copy + xsl:apply-templates,让子节点也能被后续模板处理:
<xsl:template match="product[matches(@id, 'C17.*[^V]$')]"> <xsl:copy> <!-- 保留product的所有属性 --> <xsl:copy-of select="@*"/> <!-- 触发子节点的模板匹配,包括你的title重命名模板 --> <xsl:apply-templates select="*"/> </xsl:copy> </xsl:template>
2. 模板匹配的优先级与顺序问题
如果你的过滤模板是一个“捕获所有”的模板(比如match="product"),而重命名<title>的模板在它之后,可能因为优先级规则没被触发?更常见的情况是,你写了一个排除不符合条件<product>的模板,但没正确传递上下文。
比如错误写法:
<xsl:template match="product"> <xsl:if test="matches(@id, 'C17.*[^V]$')"> <xsl:copy-of select="."/> <!-- 还是copy-of的老问题 --> </xsl:if> </xsl:template>
改成精准匹配符合条件的product,再结合xsl:apply-templates就没问题。
3. 命名空间或模板模式(Mode)的遗漏
如果你的XML里带命名空间,而模板匹配没指定对应命名空间;或者你给过滤模板加了mode属性(比如mode="filter"),但重命名<title>的模板没对应这个mode,那也不会被调用。
比如如果过滤用了mode:
<xsl:template match="product[matches(@id, 'C17.*[^V]$')]" mode="filter"> <xsl:copy> <xsl:copy-of select="@*"/> <!-- 要指定对应mode才能触发子模板 --> <xsl:apply-templates select="*" mode="filter"/> </xsl:copy> </xsl:template> <!-- 这里也要加上对应的mode --> <xsl:template match="title" mode="filter"> <new-title><xsl:value-of select="."/></new-title> </xsl:template>
4. 上下文节点的意外丢失
有时候在复杂逻辑里,你可能用了xsl:for-each或者变量存储节点,之后没正确地对节点应用模板。比如:
<xsl:variable name="valid-products" select="//product[matches(@id, 'C17.*[^V]$')]"/> <xsl:for-each select="$valid-products"> <xsl:copy-of select="."/> <!-- 依然是copy-of导致子模板不触发 --> </xsl:for-each>
同样要换成xsl:copy + xsl:apply-templates来处理子节点。
你可以先检查最常见的xsl:copy-of问题,这大概率是罪魁祸首。如果还是不行,可以把你的完整XSLT片段贴出来,我再帮你精准定位!
内容的提问来源于stack exchange,提问作者Adrian

