Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSL, using XML as parameter to template

Is it OK to pass XML to an XSL template through a parameter? For example, below I have the template body call template test1 which passes some XML through the parameter var1. I then attempt to walk to the node a using XPATH

<xsl:template name="test1">
    <xsl:param name="var1" />
    <fo:block>
        <xsl:value-of select="$var1/a" />
    </fo:block>
</xsl:template>

<xsl:template name="body">
    <xsl:call-template name="test1">
        <xsl:with-param name="var1" >
            <a>foo</a>
        </xsl:with-param>
    </xsl:call-template>
</xsl:template>

The problem is, this causes my XSL interpreter to fail with an unuseful error message. I'm being forced to use a closed source vendor system for generating documents, so I'm not able to debug. But before I look into it more, I'm looking for confirmation that this is valid usage of XSL.

like image 909
Mike Avatar asked Sep 19 '10 18:09

Mike


1 Answers

<xsl:template name="body"> 
    <xsl:call-template name="test1"> 
        <xsl:with-param name="var1" > 
            <a>foo</a> 
        </xsl:with-param> 
    </xsl:call-template> 
</xsl:template>

The $var1 parameter passed to the template is hardly usable in XSLT 1.0/XPath 1.0 due to the infamous RTF (Result-Tree Fragment) type.

From the W3C XSLT 1.0 spec.:

11.1 Result Tree Fragments

Variables introduce an additional data-type into the expression language. This additional data type is called result tree fragment. A variable may be bound to a result tree fragment instead of one of the four basic XPath data-types (string, number, boolean, node-set). A result tree fragment represents a fragment of the result tree. A result tree fragment is treated equivalently to a node-set that contains just a single root node. However, the operations permitted on a result tree fragment are a subset of those permitted on a node-set. An operation is permitted on a result tree fragment only if that operation would be permitted on a string (the operation on the string may involve first converting the string to a number or boolean). In particular, it is not permitted to use the /, //, and [] operators on result tree fragments. When a permitted operation is performed on a result tree fragment, it is performed exactly as it would be on the equivalent node-set.

When a result tree fragment is copied into the result tree (see [11.3 Using Values of Variables and Parameters with xsl:copy-of]), then all the nodes that are children of the root node in the equivalent node-set are added in sequence to the result tree.

Expressions can only return values of type result tree fragment by referencing variables of type result tree fragment or calling extension functions that return a result tree fragment or getting a system property whose value is a result tree fragment.

To circumvent this crippling design decision, almost every XSLT processor has its own extension function, usually named xxx:node-set() where the xxx prefix must be bound to a vendor-defined namespace.

Solution:

Find exactly what is the name of this extension function offered by your XSLT processor vendor.

Or, if your XSLT processor supports EXSLT, use the exsl:node-set() extension function as defined by EXSLT.

Here are some vendor-specific namespaces:

MSXML and .NET XslCompiledTransform, XslCompiledTransform:

xmlns:vendor="urn:schemas-microsoft-com:xslt"

Xalan (note, the function name is: xxx:nodeset() !):

 xmlns:vendor="http://xml.apache.org/xalan"

Saxon (6.x):

 xmlns:vendor="http://icl.com/saxon"

Altova:

 xmlns:vendor="http://www.altova.com/xslt-extensions"

All XSLT processors that implement EXSLT:

xmlns:vendor="http://exslt.org/common"

In case the body of the <xsl:param> isn't dynamically constructed, one can avoid the

xxx:node-set() function in the following way:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>

 <xsl:variable name="vrtfparamXML">
   <a>
     <b>foo</b>
   </a>
 </xsl:variable>

 <xsl:variable name="vparamXML" select=
 "document('')/*/xsl:variable
                [@name='vrtfparamXML']/*"/>

 <xsl:template match="/">
   <xsl:copy-of select="$vparamXML/b"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on any XML document (not used), the result is the wanted child of the RTF:

<b xmlns:xsl="http://www.w3.org/1999/XSL/Transform">foo</b>

Note: XSLT 2.0/XPath 2.0 got rid of the RTF "type" and there one doesn't have any problem accessing temporary trees and navigating them using the full power of XPath 2.0

like image 141
Dimitre Novatchev Avatar answered Nov 11 '22 15:11

Dimitre Novatchev