How to Evaluate Dynamic XPath in XSLT 3.0?
XSLT 3.0 introduced the <xsl:evaluate>
instruction, providing a standard, native mechanism to construct and
execute dynamic XPath expressions at runtime without relying on
proprietary vendor extensions like saxon:evaluate or EXSLT.
This article outlines the architecture and syntax of
<xsl:evaluate>, demonstrates how to supply context
items and dynamic parameters, and details essential security and
performance practices for running dynamic expressions in XML
pipelines.
The Need for Dynamic XPath in XSLT
In earlier versions of XSLT (1.0 and 2.0), all XPath expressions in templates, select attributes, and match patterns had to be static strings known at stylesheet compile time. When applications required configurable business rules, user-defined filtering, or runtime path resolution from external configuration files, developers were forced to generate and compile stylesheets on the fly or rely on non-standard extension functions.
The <xsl:evaluate> instruction standardizes
dynamic evaluation across all compliant XSLT 3.0 processors. It takes an
XPath expression supplied as a runtime string, parses it, evaluates it
against a specified context, and returns the result to the
transformation.
Syntax and Key Attributes
The basic syntax of <xsl:evaluate> revolves around
the xpath attribute along with several optional
configuration attributes:
<xsl:evaluate xpath="string-expression"
as="sequence-type"
context-item="expression"
namespace-context="element-node"
schema-aware="yes | no">
<xsl:with-param name="qname" select="expression"/>
</xsl:evaluate>xpath: Specifies the dynamic XPath expression to be evaluated. It is evaluated as a string; when using attributes or variables directly, attribute value templates (xpath="{$expr}") or standard string expressions (xpath="concat('//', $elementName)") are used.context-item: Sets the initial context item (.) for the dynamic expression. If omitted, the dynamic expression evaluates within the current context of the stylesheet.as: Defines the expected return type (for example,xs:boolean,element()*, orxs:string). Supplying an explicit type ensures compile-time and runtime type safety.namespace-context: Directs the processor to use the in-scope namespaces of a specified element node when resolving prefixes inside the dynamic string.
Practical Example: Dynamic Field Filtering
Consider an XML document containing employee records where the filtering criterion is passed dynamically as a parameter to the stylesheet.
Input XML
<company>
<employee id="101" department="Engineering" active="true">
<name>Alice Smith</name>
<salary>95000</salary>
</employee>
<employee id="102" department="Marketing" active="false">
<name>Bob Jones</name>
<salary>72000</salary>
</employee>
<employee id="103" department="Engineering" active="true">
<name>Charlie Brown</name>
<salary>88000</salary>
</employee>
</company>XSLT 3.0 Stylesheet
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" indent="yes"/>
<!-- Dynamic filter expression passed as parameter -->
<xsl:param name="filterCriteria" as="xs:string" select="'@department = ''Engineering'' and salary > 90000'"/>
<xsl:template match="/company">
<filtered-employees>
<xsl:for-each select="employee">
<xsl:variable name="matches" as="xs:boolean">
<xsl:evaluate xpath="$filterCriteria" context-item="." as="xs:boolean"/>
</xsl:variable>
<xsl:if test="$matches">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</filtered-employees>
</xsl:template>
</xsl:stylesheet>Passing Variables Using
<xsl:with-param>
Dynamic expressions often need access to values calculated inside the
stylesheet. Rather than concatenating raw values into the dynamic XPath
string, parameters should be passed explicitly using child
<xsl:with-param> elements. Inside the dynamic XPath
string, these parameters are referenced with standard variable syntax
($paramName).
<xsl:variable name="threshold" select="80000" as="xs:integer"/>
<xsl:variable name="dynamicQuery" select="'salary >= $minSalary'" as="xs:string"/>
<xsl:evaluate xpath="$dynamicQuery" context-item="employee" as="xs:boolean">
<xsl:with-param name="minSalary" select="$threshold"/>
</xsl:evaluate>This approach avoids string-escaping issues, preserves data types without unnecessary conversions to strings, and prevents syntax breakage caused by unexpected characters.
Managing Namespaces
Dynamic XPath expressions that reference prefixed elements or
attributes require namespace resolution. By default,
<xsl:evaluate> uses the static in-scope namespaces of
the <xsl:evaluate> element itself.
When evaluating paths defined in external documents with arbitrary
namespaces, the namespace-context attribute can point to an
element node in the source document to inherit its active namespace
bindings:
<xsl:evaluate xpath="$externalXPath"
context-item="."
namespace-context="/*"/>Performance and Security Considerations
While <xsl:evaluate> adds significant flexibility,
it introduces specific operational trade-offs:
- Compilation Overhead: Dynamic expressions cannot be pre-compiled during the initial stylesheet compilation phase. The processor must parse and compile the expression at runtime. When executed repeatedly within loops, some processors cache compiled forms of identical dynamic strings, but dynamic evaluation remains slower than static XPath.
- XPath Injection: Constructing XPath strings via raw
string concatenation using untrusted user inputs can lead to XPath
injection attacks, potentially exposing sensitive nodes or executing
unauthorized operations. Parameter passing via
<xsl:with-param>should always be preferred over manual string concatenation. - Type Safety: Unchecked dynamic expressions can
yield runtime type mismatches. Always declare the
asattribute to validate the output sequence against the stylesheet’s expected schema types.