XSL named parameter 'with-param' using 'apply-templates'

梦想的初衷 提交于 2019-11-30 06:36:31

My questions in regards to this:

  1. Is it possible to send a named parameter to all of my xsl:templates using an xsl:apply-templates with xsl:with-param, but select this value specifically by name= within the actual template so that it can be explicitly used in a single template and ignored by all others (even if I wanted to add other, differently named, parameters for other templates later)?

Yes. In XSLT 2.0 one may use the so called "tunnel parameters", but in XSLT 1.0 this is the way to have some parameters reach some remote template down the chain.

Another way is to have global parameters, so that they wouldn't have to be passed through every template in the chain.

.2. What am I doing wrong with my current sample code that it does not seem to receive the parameter at all?

The reason is in the code you haven't shown to us. Or maybe the source XML document you have in your real case isn't the same as the one provided here. I ran the provided code and I couldn't repro the problem at all -- the desired output is produced.

My guess is that some other template is selected before the template that matches testNode. This template doesn't know anything about the passed parameter and it doesn't pass it to the templates that it, on its turn, applies. Thus the parameter is not passed at all to the template matching testNode.

My guess is that if you replace:

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

with

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

you could get the desired output.

Also, you could trace with an XSLT debugger (such as the one in Visual Studio) and see exactly which template is selected.

.3. Is there a better way to accomplish this?

As I said earlier, global parameters can be used as alternative -- I am not sure that this is better, though.

Finally, here is the code that I ran that cannot repro your problem:

XSLT stylesheet:

<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 document:

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

Result:

  This is text1
  TEST_PARAMETER
  This is text2

UPDATE:

The OP has provided more accurate information which prooves my guess.

Now it is obvious that the problem is caused by allowing the XSLT built-in template for element node to be selected for wrapperNode. This template, naturally, doesn't know about any parameters and it doesn't use the testParam parameter nor does it pass this parameter through. Thus, the <xsl:apply-templates/> in the built-in template causes the template matching testNode to be selected without passing any parameter to it. THis explains the reported behavior/result.

Solution: The solution is to specify a template matching wrapperNode that accepts a parameter named testParam and passes it through when it applies templates:

<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>

Now when this transformation is applied on the provided XML document, the expected result is produced:

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

Your <html> and <body> tags can't occur where they are in an XSL stylesheet. When I remove them and the closing tags and run this in Oxygen/XML I get your "desired" output. I think you want to put those tags INSIDE the top-level template, in which case it would generate the output within html and body tags.

Which XSLT engine did you use that didn't complain about the invalid stylesheet?

I ran the poster's 3 samples above as is and got this result using Notepad++ 7.7.1 on Wine under Ubuntu 18.04 with the XML Tools installed.

<html><body>

  TEST1
  TEST_PARAMETER
  TEST2

</body></html>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!