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:

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>:

  1. No URI Collisions: A stylesheet must not write to the same href target URI more than once during a single transformation. Attempting to write multiple result documents to identical file paths triggers a dynamic error.
  2. No Overwriting the Principal Output: The href attribute cannot resolve to the same destination as the transformation's primary result tree.
  3. 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.
  4. No Nesting: An <xsl:result-document> cannot be placed directly or indirectly inside another <xsl:result-document> instruction without completing the outer document context.