XSLT Reference
xsl:fork
Processes a streamed sequence in multiple independent branches simultaneously, enabling multiple aggregations in a single pass over a large document.
<xsl:fork>...</xsl:fork>Description
xsl:fork solves a fundamental streaming constraint: you can only read a stream once. If you need to compute two different aggregations (say, a count and a sum) over the same streaming input, you would normally need two passes — but streaming prohibits that. xsl:fork allows both operations to happen in a single pass by running multiple sequence constructors over the same streamed input simultaneously.
Each direct child sequence constructor of xsl:fork is evaluated independently against the same input. The results of all branches are concatenated in document order. Branches must be individually streamable — they cannot share state with each other except via accumulators.
xsl:fork is only meaningful in a streaming context. Outside streaming it works but is equivalent to simply writing the branches sequentially. The real value is inside xsl:stream or inside a streamable template where the input can only be consumed once.
A common pattern is xsl:fork containing two or more xsl:sequence children, each with a streamable select expression (such as count(//element) or sum(//amount)). Each select is computed in parallel over the single stream.
Attributes
xsl:fork has no element-specific attributes. Its children are sequence constructors forming the independent branches.
Examples
Computing count and sum in one streaming pass
Stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="source-uri" select="'transactions.xml'"/>
<xsl:template name="xsl:initial-template">
<xsl:stream href="{$source-uri}">
<xsl:variable name="stats">
<xsl:fork>
<xsl:sequence select="count(//transaction)"/>
<xsl:sequence select="sum(//transaction/@amount)"/>
</xsl:fork>
</xsl:variable>
<summary>
<count><xsl:value-of select="$stats[1]"/></count>
<total><xsl:value-of select="$stats[2]"/></total>
</summary>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>
Fork with apply-templates branches
Stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:mode name="count-mode" streamable="yes"/>
<xsl:mode name="list-mode" streamable="yes"/>
<xsl:param name="source-uri" select="'catalog.xml'"/>
<xsl:template name="xsl:initial-template">
<xsl:stream href="{$source-uri}">
<report>
<xsl:fork>
<xsl:apply-templates select="." mode="count-mode"/>
<xsl:apply-templates select="." mode="list-mode"/>
</xsl:fork>
</report>
</xsl:stream>
</xsl:template>
<xsl:template match="/" mode="count-mode">
<total-items><xsl:value-of select="count(//item)"/></total-items>
</xsl:template>
<xsl:template match="/" mode="list-mode">
<xsl:for-each select="//item">
<item><xsl:value-of select="@name"/></item>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Notes
- Each branch of
xsl:forkmust be independently streamable. The branches cannot share variables or communicate except through accumulators. - The results of all branches are concatenated in the order the branches appear.
xsl:forkcan containxsl:fallbackto handle non-streaming processors gracefully.- Using
xsl:forkoutside a streaming context is valid but adds no value — branches execute sequentially. - Processors may or may not truly parallelise the branches; the specification only requires that the result is as-if they ran simultaneously.