How to Split Files with xsl:result-document?
XSLT 2.0 introduced the <xsl:result-document>
instruction to solve the long-standing limitation of producing only a
single output document per transformation. By directing specific result
trees to separate URIs, a stylesheet can read a single XML source and
automatically partition it into multiple output files—such as distinct
HTML pages, XML fragments, or text logs—within a single execution. This
article explains how the instruction operates, details its primary
attributes, demonstrates a practical splitting example, and covers
essential execution rules.
Core Mechanism of xsl:result-document
In XSLT 1.0, generating secondary documents required non-standard
processor extensions (such as EXSLT's exsl:document or
Saxon's saxon:output). XSLT 2.0 standardized this behavior
through <xsl:result-document>.
When the XSLT processor encounters
<xsl:result-document>, it diverts the output
generated by its child instructions away from the principal result
stream and routes it into a newly created secondary result tree. The
processor creates and serializes this new target file according to
specified attributes before continuing with the rest of the
transformation.
Key Attributes and Configuration
The <xsl:result-document> element supports several
attributes to control target locations, formats, and serialization
behavior:
href: Specifies the URI (relative or absolute) of the output file. Relative paths resolve against the base output URI of the transformation. This attribute accepts Attribute Value Templates (AVTs) likehref="output/{chapter/@id}.html".format: References the name of an<xsl:output>declaration defined elsewhere in the stylesheet, inheriting its serialization parameters (such asmethod,encoding, orindent).method: Overrides or sets the output format directly (xml,html,xhtml, ortext).encoding: Defines the character encoding for the specific output file (e.g.,UTF-8,ISO-8859-1).indent: Acceptsyesornoto control whitespace pretty-printing.
Practical Implementation Example
Consider an XML document containing a collection of documentation chapters:
<book>
<chapter id="intro">
<title>Introduction</title>
<content>Welcome to the guide.</content>
</chapter>
<chapter id="setup">
<title>Installation</title>
<content>Steps for setup.</content>
</chapter>
</book>The following XSLT 2.0 stylesheet splits each chapter into its own HTML file while using the main template to trigger the process:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/book">
<xsl:apply-templates select="chapter"/>
</xsl:template>
<xsl:template match="chapter">
<xsl:result-document href="pages/{@id}.html">
<html>
<head>
<title><xsl:value-of select="title"/></title>
</head>
<body>
<h1><xsl:value-of select="title"/></h1>
<p><xsl:value-of select="content"/></p>
</body>
</html>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>When run, the processor produces pages/intro.html and
pages/setup.html in a single pass.
Critical Rules and Constraints
To prevent race conditions and non-deterministic behavior, the XSLT
2.0 specification enforces specific restrictions on
<xsl:result-document>:
- No URI Collisions: A stylesheet must not write to
the same
hreftarget URI more than once during a single transformation. Attempting to write multiple result documents to identical file paths triggers a dynamic error. - No Overwriting the Principal Output: The
hrefattribute cannot resolve to the same destination as the transformation's primary result tree. - Prohibited Inside Functions and Variables:
<xsl:result-document>cannot be evaluated within an<xsl:function>or inside temporary trees assigned to variables, as instructions inside functions and variables must remain side-effect free. - No Nesting: An
<xsl:result-document>cannot be placed directly or indirectly inside another<xsl:result-document>instruction without completing the outer document context.