Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameters not being passed into template when using the .Net transform classes

Tags:

xslt

I am using the .Net XslCompiledTranform to run some simple XSLT (see below for a simplified example).

The example XSLT is meant to do simply show the value of the parameter that is passed in to the template. The output is what I expect it to be (i.e.

<result xmlns:p1="http://www.doesnotexist.com">
  <valueOfParamA>valueA</valueOfParamA>
</result>

when I use Saxon 9.0, but when I use XslCompiledTransform (XslTransform) in .net I get

<result xmlns:p1="http://www.doesnotexist.com">
  <valueOfParamA></valueOfParamA>
</result>

The problem is that that the parameter value of paramA is not being passed into the template when I use the .Net classes. I completely stumped as to why. when I step through in Visual Studio, the debugger says that the template will be called with paramA='valueA' but when execution switches to the template the value of paramA is blank.

Can anyone explain why this is happening? Is this a bug in the MS implementation or (more likely) am I doing something that is forbidden in XSLT?

Any help greatly appreciated.

This is the XSLT that I am using

<?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>
like image 664
Chris Fewtrell Avatar asked Oct 15 '22 08:10

Chris Fewtrell


1 Answers

There is nothing strange -- this is the expected behavior of any XSLT 1.0 -compliant processor.

Explanation: The $nodeset variable defined as:

<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>

in XSLT 1.0 contains a complete xml document -- a document node, denoted in XPath 1.0 by / .

Therefore,

<xsl:apply-templates select="$nodeset" mode="AParticularMode">
  <xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>

Will aplly a template matching the tree (the document node /) in the specified mode, if such template exists. In your case no such template exists. Therefore the built-in XSLT 1.0 template for / is applied (which belongs to every mode).

The text of the built-in template can be found in the spec:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

As per spec: "There is also a built-in template rule for each mode, which allows recursive processing to continue in the same mode in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule for mode m.

<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

"

Of course, the built-in template doesn't know anything about your parameter $paramA and it doesn't pass it down to the applied templates.

Thus, finally, your template matching p1:foo" in mode="AParticularMode" is selected for processing. Nothing is passed as value for the parameter, so it has no value -- thus the <xsl:value-of> doesn't produce even a single character or node.

To correct this problem, simply add a template matching / and in mode "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>

and now you get the desired result.

In XSLT 2.0 (Saxon 9) you observe different behavior, because the built-in templates in XSLT 2.0 by definition retransmit all parameters with which they were applied -- see the XSLT 2.0 Spec :

"If the built-in rule was invoked with parameters, those parameters are passed on in the implicit xsl:apply-templates instruction."

like image 72
Dimitre Novatchev Avatar answered Oct 18 '22 23:10

Dimitre Novatchev