Namespace handling is the single most common source of silent failures in XSLT stylesheets. A template that looks correct will produce empty output if the namespace declarations in the stylesheet do not match the namespaces in the source document. This note covers the specific patterns that cause problems and the practices that prevent them.
The Core Issue
XSLT template matching is namespace-aware. The match pattern match="invoice" matches an element named invoice in no namespace. If the source document declares a default namespace:
<invoice xmlns="urn:example:invoices">
<line>...</line>
</invoice>
Then the invoice element is in the urn:example:invoices namespace, and the bare match="invoice" pattern does not match it. The template fires zero times. No error is reported. The output is simply missing.
This catches even experienced developers when they encounter a new source document format with namespace declarations they did not expect.
The Fix
Declare the source document’s namespaces in your stylesheet and use prefixed match patterns:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:inv="urn:example:invoices"
version="1.0">
<xsl:template match="inv:invoice">
<!-- Now this matches -->
</xsl:template>
</xsl:stylesheet>
The prefix name does not matter. It does not need to match the prefix (or lack thereof) used in the source document. What matters is that the namespace URI is identical.
Default Namespace Trap
XPath 1.0 does not support a default namespace for unprefixed names in expressions. This means that even if you declare a default namespace in your stylesheet element, unprefixed names in XPath expressions will still match elements in no namespace.
This is counterintuitive. In the source XML, unprefixed elements inherit the default namespace. In XPath 1.0, unprefixed names always refer to no namespace. The result is that XPath expressions appear to work during development (when tested without namespaces) and fail in production (when real documents have namespace declarations).
The solution: always use explicit prefixes for namespaced elements in XPath expressions. Never rely on default namespace behavior in XSLT 1.0.
XSLT 2.0 introduces xpath-default-namespace which fixes this issue. If you can use XSLT 2.0, use it. If you are stuck on XSLT 1.0, prefix everything.
Multiple Namespace Documents
Documents that use multiple namespaces (common in UBL, SOAP, and publishing XML) require all relevant namespaces to be declared in the stylesheet. Missing even one namespace declaration means all elements in that namespace become invisible to template matching.
A practical approach: open the source document, list every distinct namespace URI, declare each one in the stylesheet with a short prefix, and use those prefixes consistently throughout all match patterns and XPath expressions.
Namespace Output Control
When producing output, you may want to exclude source namespaces from the result. Use exclude-result-prefixes on the xsl:stylesheet element:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:inv="urn:example:invoices"
exclude-result-prefixes="inv"
version="1.0">
Without this, namespace declarations from the source will propagate into the output, which is usually undesirable for HTML output and sometimes problematic for XML output targeting a different vocabulary.
Debugging Namespace Issues
When a template produces no output for elements that clearly exist in the source:
- Check whether the source elements have a namespace (look for
xmlnsdeclarations). - Verify that your stylesheet declares the same namespace URI.
- Verify that your match patterns and XPath expressions use the correct prefix.
- Add
xsl:messagewithnamespace-uri(.)to confirm the runtime namespace of nodes.
For related context, see the XSLT debugging workflow guide and the XML reference section on schemas and namespaces.