XSLT同一路径多匹配模板问题:如何让节点应用所有匹配模板
嘿,这个问题我太熟了!XSLT的模板匹配规则默认就是只会选一个最匹配的模板执行,而不是所有匹配的都跑一遍——如果多个模板优先级相同,就选XSL文件里最后出现的那个,这就是你遇到的“看顺序”问题。
本质上,XSLT的模板匹配是“竞争式”的:处理器会给每个匹配的模板打分(优先级),然后挑最高分的执行;分数一样的话,就选定义靠后的。默认不会自动触发多个模板,得你手动告诉它要继续执行其他匹配的模板。
方案1:XSLT 2.0及以上(推荐,更简洁)
用<xsl:next-match/>指令,它能让处理器在当前模板执行完后,继续应用下一个匹配的模板。比如针对你的示例:
先看你的XML源:
<a> <b url="foo"> <text>1</text> <text>2</text> </b> </a>
然后写XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 第一个模板:输出所有text子节点的内容 --> <xsl:template match="//*[text]"> <xsl:for-each select="text"> <xsl:value-of select="."/> </xsl:for-each> <!-- 关键:告诉处理器继续找下一个匹配的模板执行 --> <xsl:next-match/> </xsl:template> <!-- 第二个模板:处理带url属性的节点 --> <xsl:template match="//*[@url]"> <xsl:text> (URL: </xsl:text> <xsl:value-of select="@url"/> <xsl:text>)</xsl:text> </xsl:template> <!-- 必须保留的默认模板:确保递归处理整个节点树 --> <xsl:template match="@*|node()"> <xsl:apply-templates select="@*|node()"/> </xsl:template> </xsl:stylesheet>
运行后会输出:12 (URL: foo),两个模板的逻辑都执行了。
方案2:XSLT 1.0(兼容旧环境)
XSLT 1.0没有<xsl:next-match/>,那我们可以把要执行的逻辑拆成命名模板,然后在主匹配模板里逐个调用:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 主匹配模板:负责调用所有需要的逻辑模板 --> <xsl:template match="//*[text]"> <!-- 调用输出text内容的模板 --> <xsl:call-template name="output-text-content"/> <!-- 如果节点有url属性,调用输出url的模板 --> <xsl:if test="@url"> <xsl:call-template name="output-url-attribute"/> </xsl:if> </xsl:template> <!-- 命名模板1:输出text子节点内容 --> <xsl:template name="output-text-content"> <xsl:for-each select="text"> <xsl:value-of select="."/> </xsl:for-each> </xsl:template> <!-- 命名模板2:输出url属性 --> <xsl:template name="output-url-attribute"> <xsl:text> (URL: </xsl:text> <xsl:value-of select="@url"/> <xsl:text>)</xsl:text> </xsl:template> <!-- 默认模板:递归处理节点树 --> <xsl:template match="@*|node()"> <xsl:apply-templates select="@*|node()"/> </xsl:template> </xsl:stylesheet>
额外提醒
别忘了保留那个默认的match="@*|node()"模板——它会递归处理XML里的所有节点和属性,不然处理器可能只会处理你明确匹配的节点,忽略其他部分。
内容的提问来源于stack exchange,提问作者Maroš Beťko
相关产品推荐
相关产品推荐

