How to Use Tunnel Parameters in Modern XSLT
In modern XSLT (XSLT 2.0 and later), tunnel parameters provide a mechanism to pass values from an ancestor template directly to a descendant template without requiring intermediate templates to explicitly declare or forward those parameters. This article explains how tunnel parameters work, demonstrates their syntax, and illustrates how they streamline XML transformation pipelines by eliminating repetitive parameter boilerplate.
The Problem with Standard Parameters
In standard XSLT parameter passing, if a template at the top level
needs to pass a parameter to a template several layers deep in the XML
tree, every intermediate template must participate in the chain. Each
intermediate template must declare <xsl:param> and
pass it forward using <xsl:with-param>. This tightly
couples templates and clutters stylesheets with redundant parameter
propagation code.
How Tunnel Parameters Work
Tunnel parameters solve this issue by allowing values to “tunnel”
through the call tree. You declare a tunnel parameter when calling or
applying templates, and only the templates that actually need the value
declare it. Intermediate templates simply invoke
<xsl:apply-templates/> or
<xsl:call-template> without mentioning the parameter
at all.
To pass a tunnel parameter: 1. Set tunnel="yes" on the
<xsl:with-param> element at the source call. 2. Allow
intermediate templates to process nodes normally without declaring the
parameter. 3. Set tunnel="yes" on the
<xsl:param> element in the destination template to
receive the value.
Implementation Example
Consider an XML document where a document-level setting must reach deeply nested child elements:
<catalog title="Spring Collection">
<category name="Apparel">
<item id="101">T-Shirt</item>
</category>
</catalog>The corresponding XSLT stylesheet passes the catalog title directly
to the <item> template across intermediate
<category> processing:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Root template: Initiates the tunnel parameter -->
<xsl:template match="/catalog">
<output>
<xsl:apply-templates select="category">
<xsl:with-param name="catalogTitle" select="@title" tunnel="yes" />
</xsl:apply-templates>
</output>
</xsl:template>
<!-- Intermediate template: No parameter declaration needed -->
<xsl:template match="category">
<section name="{@name}">
<xsl:apply-templates select="item" />
</section>
</xsl:template>
<!-- Target template: Receives the tunnel parameter -->
<xsl:template match="item">
<xsl:param name="catalogTitle" tunnel="yes" />
<product catalog="{$catalogTitle}">
<xsl:value-of select="." />
</product>
</xsl:template>
</xsl:stylesheet>Overriding Tunnel Parameters
Tunnel parameters remain active down the execution tree until
explicitly overridden. If an intermediate template needs to modify or
replace the tunneled value for subsequent descendants, it can issue a
new
<xsl:with-param name="..." select="..." tunnel="yes"/>
in its own <xsl:apply-templates> call. Descendants
below that point will receive the newly defined value, while sibling
branches retain the original value.