开发者

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

I am u开发者_JS百科sing 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>


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


Answer for your question,

Why my first attempt failed?

As you are using node-set() comfortably in your code I guess you might be well aware of Result Tree Fragment. If not, then go through this link.

Well. By taking advantage of RTF[Result tree fragment] you are able to treat "foo" as a node.

The variable $nodeset has stored the tree-structure of the node, so that you can treat it's value as node-set, where as variable $nodeset is still a variable. If you want to apply-template then apply on, it's child nodes[precisely elements] appearing as it's value,

Instead of * you could have used,

<xsl:apply-templates select="$nodeset/p1:foo" mode="AParticularMode">

This is more precise,


Well after more experimenting I found that altering the apply-templates to

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

(note the select="$nodeset/*" instead of select="nodeset") made it work as I wanted it to in .Net and Saxon.

I would however still be grateful if someone can explain why my first attempt failed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜