我正在使用.Net XslCompiledTranform来运行一些简单的XSLT(参见下面的简化示例)。
示例XSLT旨在简单地显示传递给模板的参数的值。输出是我所期望的(即
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA>valueA</valueOfParamA>
</result>
当我使用Saxon 9.0时,但是当我在.net中使用XslCompiledTransform(XslTransform)时,我得到了
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA></valueOfParamA>
</result>
问题是当我使用.Net类时,paramA的参数值没有传递到模板中。我完全难以理解为什么。当我在Visual Studio中单步执行时,调试器表示将使用paramA ='valueA'调用模板,但是当执行切换到模板时,paramA的值为空。
任何人都可以解释为什么会这样吗?这是MS实现中的错误还是(更有可能)我在做XSLT中禁止的事情?
非常感谢任何帮助。
这是我正在使用的XSLT
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:extfn="http://exslt.org/common" exclude-result-prefixes="extfn" xmlns:p1="http://www.doesnotexist.com">
<!--
Replace msxml with
xmlns:extfn="http://exslt.org/common"
xmlns:extfn="urn:schemas-microsoft-com:xslt"
-->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="resultTreeFragment">
<p1:foo>
</p1:foo>
</xsl:variable>
<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>
<result>
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="p1:foo" mode="AParticularMode">
<xsl:param name="paramA"/>
<valueOfParamA>
<xsl:value-of select="$paramA"/>
</valueOfParamA>
</xsl:template>
</xsl:stylesheet>
答案 0 :(得分:4)
没有什么奇怪的 - 这是任何符合XSLT 1.0标准的处理器的预期行为。
<强>解释强>:
$nodeset
变量定义为:
<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>
包含一个完整的 xml文档 - 一个文档节点,由/
在XPath 1.0中表示。
因此,
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
如果存在此类模板,将在指定模式下使用与树(文档节点/
)匹配的模板。在您的情况下,不存在此类模板。因此,应用了/
的内置XSLT 1.0模板(属于每个模式)。
可以在spec:
中找到内置模板的文字<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
根据规格: “每个模式还有一个内置模板规则,它允许递归处理在样式表中的显式模板规则没有成功模式匹配的情况下以相同模式继续。此模板规则适用于两者元素节点和根节点。以下显示模式m 的内置模板规则的等效项。
<xsl:template match="*|/" mode="m">
<xsl:apply-templates mode="m"/>
</xsl:template>
“
当然,内置模板对您的参数$paramA
一无所知,也不会将其传递给应用的模板。
因此,最后,选择p1:foo"
中与mode="AParticularMode"
匹配的模板进行处理。没有任何东西作为参数的值传递,因此它没有值 - 因此<xsl:value-of>
甚至不会产生单个字符或节点。
要解决此问题,只需添加与/
匹配且模式为"AParticularMode"
的模板:
<xsl:template match="/" mode="AParticularMode">
<xsl:param name="paramA"/>
<xsl:apply-templates mode="AParticularMode">
<xsl:with-param name="paramA" select="$paramA"/>
</xsl:apply-templates>
</xsl:template>
现在您可以获得所需的结果。
在XSLT 2.0(Saxon 9)中,您观察到不同的行为,因为XSLT 2.0中的内置模板根据定义重新传输了应用它们的所有参数 - 请参阅 {{ 3}} :
“如果使用参数调用内置规则,那些参数 在隐式的xsl:apply-templates指令中传递。“
答案 1 :(得分:0)
经过更多的实验,我发现将apply-templates更改为
<xsl:apply-templates select="$nodeset/*" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
(请注意select="$nodeset/*"
而不是select="nodeset"
)使其在.Net和Saxon中按照我的意愿运行。
如果有人可以解释为什么我的第一次尝试失败,我仍会感激不尽。
答案 2 :(得分:0)
回答你的问题,
为什么我的第一次尝试失败了?
当你在代码中使用node-set()时,我猜你可能已经清楚了解Result Tree Fragment。如果没有,请浏览this link。
好。通过利用RTF [结果树片段],您可以将“foo”视为节点。
变量$ nodeset已存储节点的树结构,因此您可以将其值视为节点集,其中变量$ nodeset仍然是变量。如果您想要然后应用apply-template,它的子节点[精确元素]显示为它的值,
而不是*你可以使用,
<xsl:apply-templates select="$nodeset/p1:foo" mode="AParticularMode">
这更准确,