XSLT - Tunnel Parameters in XSLT 2.0 and 3.0
Tunnel parameters are an advanced XSLT feature used to pass a parameter through several levels of template processing without requiring every intermediate template to explicitly receive and forward that parameter. They are particularly useful when a value is needed deep inside a hierarchy of templates, but the intermediate templates do not actually need to use that value themselves.
The W3C XSLT specification describes tunnel parameters as parameters that are passed transparently through multiple template invocations. A tunnel parameter is created by using tunnel="yes" with xsl:with-param, and the receiving template must declare the corresponding xsl:param with tunnel="yes". (W3C)
1. Understanding the Problem
Consider an XML document containing departments, employees, and contact information:
<company>
<department name="Sales">
<employee>
<name>John</name>
</employee>
<employee>
<name>Mary</name>
</employee>
</department>
</company>
Suppose we want to pass a value such as a department-specific display format to the employee template.
Without tunnel parameters, we might have to pass the parameter through every template:
<xsl:template match="department">
<xsl:apply-templates>
<xsl:with-param name="format" select="'compact'"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="employee">
<xsl:param name="format"/>
<xsl:apply-templates select="name">
<xsl:with-param name="format" select="$format"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="name">
<xsl:param name="format"/>
...
</xsl:template>
Here, the employee template may not actually need the format value. It is only forwarding it to the name template.
This can become cumbersome when the XML structure contains many levels.
Tunnel parameters solve this problem.
2. What Is a Tunnel Parameter?
A normal template parameter is passed explicitly from one template to another.
A tunnel parameter, on the other hand, can travel through intermediate template calls automatically.
The basic idea is:
Template A
|
| tunnel parameter
v
Template B
|
| automatically carried
v
Template C
|
| automatically carried
v
Template D
Template D can access the parameter even though Templates B and C do not explicitly pass it onward.
This is the key characteristic of tunnel parameters. The W3C specification states that they allow values to be passed through multiple template invocations without requiring each intermediate template to be aware of the parameter. (W3C)
3. Syntax of a Tunnel Parameter
A tunnel parameter requires two important declarations.
The calling template uses:
<xsl:with-param name="parameter-name"
select="value"
tunnel="yes"/>
The receiving template uses:
<xsl:param name="parameter-name"
tunnel="yes"/>
For example:
<xsl:apply-templates>
<xsl:with-param name="display-mode"
select="'compact'"
tunnel="yes"/>
</xsl:apply-templates>
The receiving template can declare:
<xsl:template match="employee">
<xsl:param name="display-mode"
tunnel="yes"/>
...
</xsl:template>
The important point is that tunnel="yes" must be used consistently when supplying and receiving the tunnel parameter.
4. Complete Example
Consider the following XML:
<company>
<department name="Sales">
<employee>
<name>John</name>
<position>Manager</position>
</employee>
</department>
</company>
We want the department template to specify a display mode and make that value available to the employee template.
An XSLT stylesheet can be written as:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<company>
<xsl:apply-templates select="company/department"/>
</company>
</xsl:template>
<xsl:template match="department">
<department>
<xsl:attribute name="name">
<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:apply-templates select="employee">
<xsl:with-param name="display-mode"
select="'compact'"
tunnel="yes"/>
</xsl:apply-templates>
</department>
</xsl:template>
<xsl:template match="employee">
<xsl:param name="display-mode"
tunnel="yes"/>
<employee>
<name>
<xsl:value-of select="name"/>
</name>
<mode>
<xsl:value-of select="$display-mode"/>
</mode>
</employee>
</xsl:template>
</xsl:stylesheet>
The resulting output would be:
<company>
<department name="Sales">
<employee>
<name>John</name>
<mode>compact</mode>
</employee>
</department>
</company>
The value compact was supplied by the department template and made available to the employee template as a tunnel parameter.
5. Why Is It Called a Tunnel Parameter?
The term "tunnel" describes how the parameter travels through the transformation.
Imagine a parameter entering a tunnel:
Starting template
|
| display-mode = "compact"
v
Template A
|
v
Template B
|
v
Template C
|
v
Final template
The intermediate templates do not have to explicitly receive the parameter and pass it forward.
This differs from ordinary parameters, where every relevant template call normally needs explicit parameter handling.
6. Normal Parameters vs Tunnel Parameters
The difference is easier to understand through an example.
Normal parameter
<xsl:apply-templates>
<xsl:with-param name="color" select="'blue'"/>
</xsl:apply-templates>
The receiving template declares:
<xsl:param name="color"/>
If that template calls another template and the next template needs the parameter, it generally has to pass it explicitly:
<xsl:apply-templates>
<xsl:with-param name="color" select="$color"/>
</xsl:apply-templates>
Tunnel parameter
The caller uses:
<xsl:with-param name="color"
select="'blue'"
tunnel="yes"/>
The receiving template uses:
<xsl:param name="color"
tunnel="yes"/>
The parameter can then continue through subsequent template calls without each intermediate template having to explicitly forward it.
This is the main practical advantage of tunnel parameters.
7. Example with Multiple Template Levels
Suppose the XML has the following structure:
<document>
<section>
<paragraph>
<text>Hello World</text>
</paragraph>
</section>
</document>
We want to specify a formatting mode at the section level but use it inside the text template.
The stylesheet could be:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="section">
<xsl:apply-templates>
<xsl:with-param name="format"
select="'uppercase'"
tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="paragraph">
<paragraph>
<xsl:apply-templates/>
</paragraph>
</xsl:template>
<xsl:template match="text">
<xsl:param name="format"
tunnel="yes"/>
<xsl:choose>
<xsl:when test="$format = 'uppercase'">
<xsl:value-of select="upper-case(.)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Notice something important here.
The paragraph template does not declare:
<xsl:param name="format" tunnel="yes"/>
It also does not explicitly pass format.
Nevertheless, the text template receives the tunnel parameter.
This is exactly the situation tunnel parameters are designed for.
8. Tunnel Parameters and Built-in Template Rules
Tunnel parameters can also pass through built-in template rules.
This is useful because XSLT often processes nodes using built-in behavior when no explicit template matches them. The XSLT specification specifically defines tunnel parameters as being passed through built-in template rules. (W3C)
For example:
<xsl:apply-templates>
<xsl:with-param name="language"
select="'en'"
tunnel="yes"/>
</xsl:apply-templates>
A deeply nested template can retrieve it with:
<xsl:param name="language"
tunnel="yes"/>
Intermediate built-in processing does not necessarily require you to manually forward the parameter.
9. Tunnel Parameters with Default Values
A tunnel parameter can have a default value.
For example:
<xsl:param name="format"
select="'normal'"
tunnel="yes"/>
If the caller does not provide the tunnel parameter, the receiving template can use its default value.
For example:
<xsl:template match="employee">
<xsl:param name="format"
select="'normal'"
tunnel="yes"/>
<xsl:value-of select="$format"/>
</xsl:template>
If no value is supplied, the result is:
normal
An important detail is that a default value is local to the template; it does not create or modify the tunnel parameter set being passed onward. (W3C)
10. Required Tunnel Parameters
A tunnel parameter can also be mandatory.
For example:
<xsl:param name="department"
tunnel="yes"
required="yes"/>
This tells XSLT that the template requires the tunnel parameter.
If the required tunnel parameter is not supplied, the transformation produces a dynamic error. The XSLT specification identifies this condition as an error when a required tunnel parameter has no matching value in the tunnel parameter set. (W3C)
This is useful when a value is essential for correct processing.
11. Tunnel Parameters with Types
XSLT 2.0 and 3.0 allow parameters to have declared types.
For example:
<xsl:param name="font-size"
as="xs:integer"
tunnel="yes"/>
The stylesheet needs the XML Schema namespace:
xmlns:xs="http://www.w3.org/2001/XMLSchema"
A complete declaration might therefore look like:
<xsl:param name="font-size"
as="xs:integer"
tunnel="yes"/>
The supplied value can be converted when necessary according to XSLT's type-conversion rules. The W3C specification also explains that the value passed onward remains the originally supplied value, while the receiving template can use the converted value according to its declared type. (W3C)
12. Overriding a Tunnel Parameter
A tunnel parameter can be given a different value at a deeper level.
For example:
<xsl:apply-templates>
<xsl:with-param name="theme"
select="'blue'"
tunnel="yes"/>
</xsl:apply-templates>
A deeper template can supply another value:
<xsl:apply-templates>
<xsl:with-param name="theme"
select="'green'"
tunnel="yes"/>
</xsl:apply-templates>
The deeper value can therefore become the effective tunnel parameter for subsequent processing.
This makes tunnel parameters useful for contextual settings that can change as the transformation moves through different parts of an XML document.
13. Tunnel Parameters and Global Variables
Tunnel parameters may look similar to global variables, but they serve a different purpose.
A global variable is declared at the stylesheet level:
<xsl:variable name="format"
select="'compact'"/>
It can be accessed wherever its scope permits.
A tunnel parameter is associated with a particular template-processing path:
<xsl:with-param name="format"
select="'compact'"
tunnel="yes"/>
This means different branches of the transformation can receive different values.
For example:
Department A
format = compact
Department B
format = detailed
This contextual behavior is one of the important reasons to choose tunnel parameters instead of simply using a global variable.
14. Tunnel Parameters Are Not Passed to Functions
An important limitation is that tunnel parameters apply to template processing. They are not automatically passed to stylesheet functions.
For example:
<xsl:function name="local:calculate">
...
</xsl:function>
does not automatically receive tunnel parameters.
If a function needs a value, it should receive that value as an ordinary function argument.
Conceptually:
Template processing
Tunnel parameters: supported
Stylesheet functions
Tunnel parameters: not automatically passed
The W3C specification explicitly states that tunnel parameters are not passed in calls to stylesheet functions. (W3C)
15. Common Use Cases
Tunnel parameters are particularly useful in large XSLT applications.
Formatting options
A parent template can establish a formatting mode:
<xsl:with-param name="format"
select="'detailed'"
tunnel="yes"/>
A deeply nested template can use that setting.
Language selection
A transformation may need to produce different text based on language:
<xsl:with-param name="language"
select="'en'"
tunnel="yes"/>
The value can travel through multiple template levels until it reaches templates that generate text.
Output configuration
A document may have different presentation requirements:
<xsl:with-param name="output-mode"
select="'print'"
tunnel="yes"/>
Nested templates can use the value without forcing every intermediate template to know about it.
Context-sensitive numbering
The XSLT specification gives an example involving equation numbering, where the formatting of equation numbers can be determined at a higher level and passed through to the template that actually generates the equation number. (W3C)
Application-specific settings
Large transformations can have contextual settings such as:
language
display mode
formatting style
output channel
document type
security context
processing mode
Tunnel parameters provide a clean way to carry these settings through a template hierarchy.
16. Advantages of Tunnel Parameters
Reduces parameter forwarding
Without tunneling, intermediate templates may need to repeatedly pass parameters.
With tunnel parameters, that unnecessary forwarding can be eliminated.
Improves stylesheet organization
Templates that do not use a particular parameter do not need to declare it merely to pass it onward.
Supports contextual configuration
Different sections of the XML document can establish different parameter values.
Useful for deeply nested transformations
The deeper the template hierarchy becomes, the more useful tunnel parameters can be.
Avoids excessive global variables
Instead of making every configuration value globally available, a value can be restricted to the relevant processing path.
17. Disadvantages and Things to Consider
Tunnel parameters should not be used for every parameter.
They can make a stylesheet harder to understand if used excessively because a template may depend on a value that was established much higher in the processing hierarchy.
For example:
<xsl:param name="mode"
tunnel="yes"/>
does not immediately tell someone reading the template where the value originated.
Therefore, tunnel parameters should generally be used when a parameter genuinely needs to travel through several intermediate templates.
For simple parent-to-child communication, ordinary parameters are often clearer.
18. Important Rules to Remember
The most important rules are:
-
Use
tunnel="yes"when supplying the parameter.
<xsl:with-param name="mode"
select="'compact'"
tunnel="yes"/>
-
Use
tunnel="yes"when receiving it.
<xsl:param name="mode"
tunnel="yes"/>
-
Tunnel parameters can pass through multiple template invocations.
-
Intermediate templates do not need to explicitly forward the parameter.
-
Tunnel parameters can have default values.
-
Tunnel parameters can be declared as required.
-
Tunnel parameters can have declared types.
-
Tunnel parameters can be used with template calls such as
xsl:apply-templates,xsl:call-template,xsl:apply-imports, andxsl:next-match. (W3C) -
Tunnel parameters are not automatically passed to stylesheet functions. (W3C)
-
Excessive use can make a stylesheet difficult to trace and maintain.
19. Tunnel Parameter vs Ordinary Parameter
| Feature | Ordinary Parameter | Tunnel Parameter |
|---|---|---|
| Declaration | xsl:param |
xsl:param tunnel="yes" |
| Passing | xsl:with-param |
xsl:with-param tunnel="yes" |
| Explicit forwarding | Usually required | Not required through intermediate templates |
| Suitable for | Direct template communication | Deep template hierarchies |
| Contextual values | Possible | Particularly suitable |
| Passed to functions | Not applicable as template parameter | Not automatically passed |
| Complexity | Simple | More advanced |
20. Final Example
A concise practical example looks like this:
<xsl:template match="department">
<xsl:apply-templates>
<xsl:with-param name="display-mode"
select="'compact'"
tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="employee">
<xsl:param name="display-mode"
tunnel="yes"/>
<employee>
<xsl:if test="$display-mode = 'compact'">
<xsl:value-of select="name"/>
</xsl:if>
</employee>
</xsl:template>
The department template establishes the display-mode. The employee template receives it as a tunnel parameter. If several other template levels exist between these two templates, they do not need to explicitly receive and forward display-mode.
In summary, tunnel parameters provide a controlled way to carry contextual information through a hierarchy of XSLT templates. They are especially valuable in complex XSLT 2.0 and XSLT 3.0 transformations where ordinary parameter passing would require repeated and unnecessary forwarding. The feature is formally defined in the W3C XSLT specification and is intended specifically for transparent parameter propagation across template processing. (W3C)