XSL使用'apply-templates'命名参数'with-param'

时间:2010-11-18 21:13:27

标签: xslt parameters stylesheet apply-templates

如果您希望在完整解释之前阅读它们,我的问题就在本文的底部。

我正在使用XSL将XML文档转换为漂亮的网页,并且无法正确传递变量。我定义了许多xsl:template,并且需要将特定参数传递给其中一个。我希望我能够传递一个命名参数,该参数可能会被发送到所有xsl:template,但只能由一个参与,而被其他人忽略。但是,当我试图为自己测试这个(以及我对XSL的有限理解)时,我根本无法传递参数,更不用说测试它是否意外地干扰了任何其他xsl:template

以下是简化的示例代码(为此问题键入,可能包含一个错字或两个)。我有许多不同的xsl:template被定义来处理XML中的节点,到目前为止一切都运行良好。正是在为这些模板添加一个参数时,我似乎遇到了问题。

XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="main.xsl"?>
<wrapperNode>
  <testNode>
    <subNode/>
  </testNode>
</wrapperNode>

main.xsl:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="test.xsl"/>
<xsl:output method="html" indent="yes"/>

<xsl:template match="/">

<html>
  <body>
      <xsl:apply-templates>
        <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param>
      </xsl:apply-templates>
  </body>
</html>

</xsl:template>
</xsl:stylesheet>

test.xsl:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="testNode">
  <xsl:param name="testParam" />
  TEST1
  <xsl:value-of select="$testParam" />
  TEST2
</xsl:template>
</xsl:stylesheet>

输出(实际):

TEST1 TEST2

输出(预期/期望):

TEST1 TEST_PARAMETER TEST2

我对此有疑问:

  1. 是否可以发送命名 我所有的参数 xsl:template使用了 xsl:apply-templates xsl:with-param,但请选择此项 特别是name=内的值 实际模板,以便它可以 明确地用在一个单一的 模板,被其他所有人忽略 (即使我想添加其他, 不同的名称,参数 其他模板稍后)?

  2. 我目前的示例代码出错了,它似乎根本没有收到参数?

  3. 有没有更好的方法来实现这一目标?

  4. 修改:我想明确指出,由于test.xsltestNode模板中的其他输出,我确信它 IS 被成功召唤。 参数部分无法正常工作。我并不是要浪费人们的时间来弄清楚为什么不调用该模板。它是。

    更新:回应我最初收到的答案,其中指出我编写的示例并不完全正确(我的错误)并且没有非常清楚地显示问题(即:正确的模板调用,但参数似乎不起作用),我用更好的示例替换了示例。此示例更清楚地显示已成功调用testNode模板,但似乎未传递参数。在考虑此问题的先前答案之前和之后,我已经多次测试过。我绝对难过,因为从我在其他地方读到的内容和到目前为止人们的建议看来,一切似乎都是正确的。

3 个答案:

答案 0 :(得分:27)

  

我对此有疑问:

     
      
  1. 是否可以将命名参数发送到我的所有xsl:templates   使用xsl:apply-templates   xsl:with-param,但选择此值   特别是名字=在...内   实际模板,以便它可以   明确用于单个模板   并被所有其他人忽视(即使我   想要以不同方式添加其他内容   named,其他模板的参数   以后)?
  2.   

即可。在XSLT 2.0中,可以使用所谓的“隧道参数”,但在XSLT 1.0中这是使某些参数到达链中的某个远程模板的方式。

另一种方法是使用全局参数,这样就不必通过链中的每个模板传递它们。

  

0.2。我目前的示例代码没有错误   似乎完全接收参数?

原因在于您未向我们展示的代码。或者,您在实际案例中拥有的源XML文档可能与此处提供的文档不同。我运行了提供的代码,我根本无法重现问题 - 产生了所需的输出。

我的猜测是在匹配testNode的模板之前选择了其他模板。此模板对传递的参数一无所知,也没有将其传递给反过来适用的模板。因此,该参数根本不会传递给匹配testNode的模板。

我的猜测是,如果你替换:

  <xsl:apply-templates> 
    <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param> 
  </xsl:apply-templates> 

  <xsl:apply-templates select="testNode"> 
    <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param> 
  </xsl:apply-templates> 

你可以获得所需的输出。

此外,您可以使用XSLT调试器(例如Visual Studio中的调试器)进行跟踪,并确切地查看选择了哪个模板。

  

0.3。有没有更好的方法来实现这一目标?

正如我之前所说,全局参数可以作为替代 - 但我不确定这是否更好。

最后,这是我运行的无法重现问题的代码:

XSLT样式表

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

 <xsl:template match="/">
      This is text1
      <xsl:apply-templates>
        <xsl:with-param name="testParam">TEST_PARAMETER</xsl:with-param>
      </xsl:apply-templates>
      This is text2
 </xsl:template>

 <xsl:template match="testNode">
  <xsl:param name="testParam" />
  <xsl:value-of select="$testParam" />
 </xsl:template>
</xsl:stylesheet>

XML文档

<?xml-stylesheet type="text/xsl" href="main.xsl"?>
<testNode>
  <subNode/>
</testNode>

<强>结果

  This is text1
  TEST_PARAMETER
  This is text2

<强>更新

OP提供了更准确的信息,这证明了我的猜测。

现在很明显,问题是由于允许为wrapperNode选择 XSLT built-in template 元素节点引起的。当然,这个模板不知道任何参数,它不使用testParam参数,也不通过这个参数。因此,内置模板中的<xsl:apply-templates/>会导致选择模板匹配testNode,而不会向其传递任何参数。这解释了报告的行为/结果。

解决方案:解决方案是指定一个匹配wrapperNode的模板,该模板接受名为testParam的参数,并在应用模板时将其传递:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>

 <xsl:template match="/">
  <html>
    <body>
        <xsl:apply-templates>
          <xsl:with-param name="testParam" select="'TEST_PARAMETER'"/>
        </xsl:apply-templates>
    </body>
  </html>
 </xsl:template>

 <xsl:template match="testNode">
  <xsl:param name="testParam" />
  TEST1
  <xsl:value-of select="$testParam" />
  TEST2
 </xsl:template>

 <xsl:template match="wrapperNode">
  <xsl:param name="testParam" />

  <xsl:apply-templates>
   <xsl:with-param name="testParam" select="$testParam"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

现在,当对提供的XML文档应用此转换时,会生成预期结果

<html>
<body>
  TEST1
  TEST_PARAMETER
  TEST2
 </body>
</html>

答案 1 :(得分:1)

您的<html><body>标记不会出现在XSL样式表中。当我删除它们和结束标签并在Oxygen / XML中运行时,我得到你的“所需”输出。我想您希望将这些标记放在顶级模板中,在这种情况下,它会在htmlbody标记内生成输出。

您使用哪种XSLT引擎并没有抱怨无效的样式表?

答案 2 :(得分:0)

我照原样运行了上面的海报的3个示例,并在Ubuntu 18.04上的Wine上使用Notepad ++ 7.7.1在安装了XML工具的情况下获得了此结果。

<html><body>

  TEST1
  TEST_PARAMETER
  TEST2

</body></html>