XSLT - Iterative Processing with <xsl:iterate> in XSLT 3.0

<xsl:iterate> is an XSLT 3.0 instruction designed for processing a sequence of items one at a time while allowing values to be carried from one iteration to the next. It provides a structured alternative to writing recursive templates or recursive functions for certain iterative problems. The W3C specification describes it as conceptually similar to tail recursion, while its constrained structure can make the processing easier for both stylesheet authors and processors to optimize. (W3C)

1. What is <xsl:iterate>?

In XSLT, you often need to process a collection of XML nodes sequentially. For example, suppose an XML document contains several employees:

<employees>
    <employee>
        <name>John</name>
        <salary>45000</salary>
    </employee>
    <employee>
        <name>Mary</name>
        <salary>52000</salary>
    </employee>
    <employee>
        <name>David</name>
        <salary>48000</salary>
    </employee>
</employees>

A traditional approach might use <xsl:for-each>:

<xsl:for-each select="employees/employee">
    <xsl:value-of select="name"/>
</xsl:for-each>

This is suitable when every item can be processed independently.

However, some problems require information from the current iteration to be passed into the next iteration. For example:

  • Maintaining a running total

  • Maintaining a counter

  • Remembering the previous item

  • Finding the highest or lowest value

  • Building a result incrementally

  • Carrying state from one item to another

  • Performing calculations where each iteration depends on the previous result

This is where <xsl:iterate> becomes particularly useful.

The basic structure is:

<xsl:iterate select="expression">
    <xsl:param name="parameter" select="initial-value"/>

    <!-- processing -->

    <xsl:next-iteration>
        <xsl:with-param name="parameter" select="new-value"/>
    </xsl:next-iteration>
</xsl:iterate>

The select expression defines the sequence to be processed. The body is then evaluated once for each item in that sequence, in order. (W3C)

2. Why Use <xsl:iterate>?

The main advantage of <xsl:iterate> is that it makes stateful sequential processing explicit.

Consider calculating a running total.

Suppose the input is:

<sales>
    <sale>100</sale>
    <sale>250</sale>
    <sale>150</sale>
    <sale>300</sale>
</sales>

The desired running totals are:

100
350
500
800

Each result depends on the result from the previous iteration.

An <xsl:iterate> solution can maintain the current total using an iteration parameter.

<xsl:iterate select="sales/sale">

    <xsl:param name="total" select="0"/>

    <total>
        <xsl:value-of select="$total + xs:decimal(.)"/>
    </total>

    <xsl:next-iteration>
        <xsl:with-param
            name="total"
            select="$total + xs:decimal(.)"/>
    </xsl:next-iteration>

</xsl:iterate>

Here, $total represents the state carried between iterations.

During the first iteration, the value is 0.

If the first sale is 100, the new value becomes:

0 + 100 = 100

The next iteration receives 100.

For the second sale:

100 + 250 = 350

The next iteration receives 350.

The process continues until all items have been processed.

3. The select Attribute

The select attribute specifies the sequence that <xsl:iterate> processes.

For example:

<xsl:iterate select="/employees/employee">

This means that each <employee> element will become the context item during an iteration.

Another example is:

<xsl:iterate select="1 to 10">

This processes the sequence:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

The sequence does not have to consist only of XML elements. XSLT 3.0 works with sequences, so the items can be nodes, strings, numbers, atomic values, or other supported XDM items.

According to the W3C specification, the body is evaluated once for every item in the input sequence unless the iteration is terminated early using <xsl:break>. (W3C)

4. Iteration Parameters with <xsl:param>

One of the most important features of <xsl:iterate> is its ability to maintain parameters between iterations.

Consider:

<xsl:iterate select="1 to 5">

    <xsl:param name="total" select="0"/>

    <value>
        <xsl:value-of select="$total + ."/>
    </value>

    <xsl:next-iteration>
        <xsl:with-param
            name="total"
            select="$total + ."/>
    </xsl:next-iteration>

</xsl:iterate>

The parameter is initially:

total = 0

During each iteration, the current number is added to the parameter.

The values evolve approximately as follows:

Iteration Current Item Previous Total New Total
1 1 0 1
2 2 1 3
3 3 3 6
4 4 6 10
5 5 10 15

The parameter therefore acts as state that moves through the sequence.

5. <xsl:next-iteration>

<xsl:next-iteration> tells XSLT to continue with the next item in the input sequence.

For example:

<xsl:next-iteration>
    <xsl:with-param
        name="total"
        select="$total + ."/>
</xsl:next-iteration>

This does two things:

  1. It tells the processor to move to the next item.

  2. It supplies a new value for the iteration parameter.

The new parameter values are then available when the next iteration begins.

The W3C specification states that <xsl:next-iteration> contributes no output itself; its primary purpose is to provide parameter values for the next iteration. (W3C)

6. Multiple Iteration Parameters

An <xsl:iterate> instruction can have multiple parameters.

For example, you might need to maintain both a total and a count:

<xsl:iterate select="sales/sale">

    <xsl:param name="total" select="0"/>
    <xsl:param name="count" select="0"/>

    <xsl:next-iteration>
        <xsl:with-param
            name="total"
            select="$total + xs:decimal(.)"/>

        <xsl:with-param
            name="count"
            select="$count + 1"/>
    </xsl:next-iteration>

</xsl:iterate>

Here:

$total

maintains the accumulated value, while:

$count

maintains the number of processed items.

This makes <xsl:iterate> useful for algorithms that require several pieces of state.

7. <xsl:on-completion>

Another important feature is <xsl:on-completion>.

It allows you to produce a result after the input sequence has been completely processed.

For example:

<xsl:iterate select="1 to 5">

    <xsl:param name="total" select="0"/>

    <xsl:on-completion>
        <result>
            <xsl:value-of select="$total"/>
        </result>
    </xsl:on-completion>

    <xsl:next-iteration>
        <xsl:with-param
            name="total"
            select="$total + ."/>
    </xsl:next-iteration>

</xsl:iterate>

The final value of $total can be used by <xsl:on-completion>.

This is especially useful when the final result depends on information accumulated during all iterations.

The W3C specification notes that <xsl:on-completion> is evaluated when the input sequence is exhausted, but it is not evaluated when processing terminates through <xsl:break>. (W3C)

8. <xsl:break> for Early Termination

Sometimes you do not want to process every item.

For example, suppose you want to search through numbers and stop when a particular condition is reached.

XSLT 3.0 provides <xsl:break> for this purpose.

<xsl:iterate select="1 to 100">

    <xsl:if test=". = 50">
        <found>
            <xsl:value-of select="."/>
        </found>

        <xsl:break/>
    </xsl:if>

</xsl:iterate>

When the current item reaches 50, <xsl:break> terminates the iteration.

Any remaining items are not processed.

This is different from <xsl:next-iteration>, which continues processing with the next item. The specification defines <xsl:break> as an instruction that ends the iteration before the remaining input items are processed. (W3C)

9. Difference Between <xsl:next-iteration> and <xsl:break>

The distinction is important.

<xsl:next-iteration> means:

Finish this iteration and continue with the next item.

<xsl:break> means:

Stop processing the sequence completely.

For example:

<xsl:choose>

    <xsl:when test="$condition">
        <xsl:break/>
    </xsl:when>

    <xsl:otherwise>
        <xsl:next-iteration/>
    </xsl:otherwise>

</xsl:choose>

The choice between these two instructions depends on whether the transformation should continue processing the remaining sequence.

10. <xsl:iterate> Compared with <xsl:for-each>

Both instructions can process sequences, but their purposes are different.

A simple <xsl:for-each> looks like this:

<xsl:for-each select="employees/employee">
    <employee>
        <xsl:value-of select="name"/>
    </employee>
</xsl:for-each>

Each employee is processed independently.

With <xsl:iterate>, you can carry information from one employee to the next:

<xsl:iterate select="employees/employee">

    <xsl:param name="previousSalary" select="0"/>

    <xsl:if test="salary > $previousSalary">
        <higher-salary>
            <xsl:value-of select="name"/>
        </higher-salary>
    </xsl:if>

    <xsl:next-iteration>
        <xsl:with-param
            name="previousSalary"
            select="salary"/>
    </xsl:next-iteration>

</xsl:iterate>

The important difference is that <xsl:iterate> provides an explicit mechanism for maintaining state.

11. <xsl:iterate> Compared with Recursion

Before <xsl:iterate>, recursive templates and functions were commonly used for algorithms requiring state.

A recursive solution conceptually looks like:

process current item
calculate new state
call the function again with new state
process next item

<xsl:iterate> expresses the same general idea more directly:

process current item
calculate new state
move to next item

The XSLT 3.0 specification describes <xsl:iterate> as conceptually behaving like a tail-recursive function. It also explains that its constrained structure can be easier for an optimizer to analyze than general recursive function calls. (W3C)

For developers learning XSLT 3.0, this can make sequential algorithms easier to understand and maintain.

12. Example: Finding the Highest Salary

Consider this XML:

<employees>
    <employee>
        <name>John</name>
        <salary>45000</salary>
    </employee>
    <employee>
        <name>Mary</name>
        <salary>62000</salary>
    </employee>
    <employee>
        <name>David</name>
        <salary>58000</salary>
    </employee>
</employees>

An iterative approach can maintain the highest salary encountered so far.

<xsl:iterate select="employees/employee">

    <xsl:param name="highestSalary" select="0"/>
    <xsl:param name="highestEmployee" select="''"/>

    <xsl:choose>

        <xsl:when test="salary > $highestSalary">

            <xsl:next-iteration>

                <xsl:with-param
                    name="highestSalary"
                    select="salary"/>

                <xsl:with-param
                    name="highestEmployee"
                    select="name"/>

            </xsl:next-iteration>

        </xsl:when>

        <xsl:otherwise>

            <xsl:next-iteration>

                <xsl:with-param
                    name="highestSalary"
                    select="$highestSalary"/>

                <xsl:with-param
                    name="highestEmployee"
                    select="$highestEmployee"/>

            </xsl:next-iteration>

        </xsl:otherwise>

    </xsl:choose>

</xsl:iterate>

The two parameters represent the current state:

highestSalary
highestEmployee

When a higher salary is found, both values are updated.

The XSLT 3.0 specification itself includes an example of using <xsl:iterate> to find employees with the highest and lowest salaries while processing each employee once. (W3C)

13. Processing the Last Item

One interesting use of <xsl:iterate> is handling the final item when ordinary look-ahead is undesirable.

For example, imagine processing paragraphs where every paragraph should be rendered normally except the final paragraph.

With streaming-oriented processing, determining whether the current item is the final item can require special handling. <xsl:iterate> can maintain the previous item and process it when the next item becomes available.

The W3C specification demonstrates this approach using an iteration parameter to retain a previous paragraph and <xsl:on-completion> to process the final retained paragraph. (W3C)

This pattern is useful when processing large sequences where you want to avoid loading the entire sequence into memory.

14. <xsl:iterate> and Streaming

One of the more advanced uses of <xsl:iterate> is stream-oriented processing.

XSLT 3.0 includes streaming capabilities intended to process large XML documents without constructing the entire source tree in memory. <xsl:iterate> can participate in such processing when used in an appropriate streamable stylesheet design. (W3C)

For example, a large transaction document could contain thousands or millions of transactions:

<transactions>
    <transaction>
        <amount>100</amount>
    </transaction>
    <transaction>
        <amount>250</amount>
    </transaction>
    ...
</transactions>

An iterative transformation could maintain an accumulated balance:

<xsl:iterate select="transactions/transaction">

    <xsl:param name="balance" select="0"/>

    <xsl:next-iteration>
        <xsl:with-param
            name="balance"
            select="$balance + xs:decimal(amount)"/>
    </xsl:next-iteration>

</xsl:iterate>

For genuinely streamable transformations, the complete stylesheet and expressions must satisfy XSLT 3.0 streaming rules; simply using <xsl:iterate> does not automatically make every transformation streamable.

15. Important Restriction on <xsl:next-iteration>

<xsl:next-iteration> cannot be placed arbitrarily inside the iteration body.

It must occur in a permitted tail position. The same restriction applies to <xsl:break>. The W3C specification defines these placement rules and reports a static error when these instructions occur outside an allowed tail position. (W3C)

For example, this structure is appropriate:

<xsl:if test="$condition">
    <xsl:next-iteration/>
</xsl:if>

But placing additional processing after the <xsl:next-iteration> in the same sequence constructor is not valid in the required tail-position sense.

This restriction exists because <xsl:next-iteration> represents a transition to the next iteration rather than an ordinary instruction that returns a value and then continues executing subsequent instructions.

16. A Complete Example

Consider the following XML:

<orders>
    <order>
        <amount>100</amount>
    </order>
    <order>
        <amount>200</amount>
    </order>
    <order>
        <amount>150</amount>
    </order>
</orders>

A complete XSLT 3.0 stylesheet could calculate the total:

<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">

        <result>

            <xsl:iterate select="orders/order">

                <xsl:param name="total" as="xs:decimal"
                           select="0"/>

                <xsl:on-completion>
                    <total>
                        <xsl:value-of select="$total"/>
                    </total>
                </xsl:on-completion>

                <xsl:next-iteration>
                    <xsl:with-param
                        name="total"
                        select="$total + xs:decimal(amount)"/>
                </xsl:next-iteration>

            </xsl:iterate>

        </result>

    </xsl:template>

</xsl:stylesheet>

The important parts are:

<xsl:iterate select="orders/order">

This specifies the sequence.

<xsl:param name="total" select="0"/>

This establishes the initial state.

<xsl:next-iteration>

This moves to the next order.

<xsl:with-param
    name="total"
    select="$total + xs:decimal(amount)"/>

This updates the state.

Finally:

<xsl:on-completion>

uses the final state after the sequence has been exhausted.

The resulting total would be:

450

17. When Should You Use <xsl:iterate>?

<xsl:iterate> is particularly appropriate when:

  • Items need to be processed in sequence.

  • The next iteration depends on information from the previous iteration.

  • You need to maintain one or more state variables.

  • You need to stop processing when a condition is reached.

  • You need a final operation after all items have been processed.

  • A recursive solution would be unnecessarily complicated.

  • You are designing certain streaming transformations.

  • You need to implement algorithms such as running totals, state tracking, sequential comparisons, or incremental calculations.

It is less useful when every item can be processed independently. In those cases, <xsl:for-each> or <xsl:apply-templates> may be simpler.

18. Key Advantages

The major advantages of <xsl:iterate> are:

Explicit state management: Parameters make it clear which values are carried between iterations.

Sequential processing: Items are processed in a defined sequence.

Early termination: <xsl:break> allows processing to stop when a condition is satisfied.

Controlled state updates: <xsl:next-iteration> allows new parameter values to be supplied for the next item.

Final processing: <xsl:on-completion> provides a convenient place for operations that depend on the final state.

Alternative to recursion: Many sequential algorithms can be expressed without manually constructing recursive templates or functions.

Potential optimization benefits: Because its structure is constrained, processors can potentially optimize iterative processing more easily than unrestricted recursive code. (W3C)

19. Important Points to Remember

When working with <xsl:iterate>, remember these rules:

  1. <xsl:iterate> was introduced in XSLT 3.0.

  2. The select attribute specifies the sequence to process.

  3. The body executes once for each item unless processing is stopped.

  4. <xsl:param> defines state for the iteration.

  5. <xsl:next-iteration> moves processing to the next item.

  6. <xsl:with-param> supplies updated state values.

  7. <xsl:break> stops the iteration early.

  8. <xsl:on-completion> can process the final state after the sequence is exhausted.

  9. <xsl:next-iteration> and <xsl:break> have specific placement restrictions.

  10. <xsl:iterate> is especially useful for stateful sequential algorithms and some streaming scenarios.

Conclusion

<xsl:iterate> is an important XSLT 3.0 feature for expressing iterative algorithms in a structured way. Its biggest strength is the ability to process a sequence while carrying state from one iteration to another. Parameters provide the state, <xsl:next-iteration> updates that state and advances processing, <xsl:break> provides early termination, and <xsl:on-completion> handles the final state.

For simple independent processing, <xsl:for-each> may be easier. But when a transformation requires running calculations, previous-item tracking, state management, early termination, or certain streaming techniques, <xsl:iterate> provides a clear and powerful approach. (W3C)