XSLT For-Each

XML data often contains many repeating elements — multiple products in a catalog, multiple employees in a directory, multiple rows in a report. The xsl:for-each instruction loops through a set of nodes and applies the same transformation to each one. It is the primary looping mechanism in XSLT 1.0.

Think of xsl:for-each like a printing machine that stamps the same layout on every item that passes through the conveyor belt. Each item gets the same format, but each carries its own unique data.

Basic Syntax

<xsl:for-each select="XPath-expression">
  <!-- Template content applied to each node -->
</xsl:for-each>

The select attribute uses XPath to identify the set of nodes to loop over. The content inside fires once for each matched node, with that node as the current context.

Simple Loop Example

XML Source: fruits.xml

<basket>
  <fruit>Mango</fruit>
  <fruit>Banana</fruit>
  <fruit>Apple</fruit>
  <fruit>Guava</fruit>
</basket>

XSLT

<xsl:template match="/">
  <html><body>
    <h2>Fruit Basket</h2>
    <ul>
      <xsl:for-each select="basket/fruit">
        <li><xsl:value-of select="."/></li>
      </xsl:for-each>
    </ul>
  </body></html>
</xsl:template>

Output

Fruit Basket
• Mango
• Banana
• Apple
• Guava

Loop Over Elements with Child Nodes

XML Source: students.xml

<class>
  <student>
    <name>Ananya Iyer</name>
    <marks>88</marks>
    <grade>A</grade>
  </student>
  <student>
    <name>Karthik Menon</name>
    <marks>74</marks>
    <grade>B</grade>
  </student>
  <student>
    <name>Zoya Sheikh</name>
    <marks>95</marks>
    <grade>A+</grade>
  </student>
</class>

XSLT

<xsl:template match="/">
  <html><body>
    <h2>Class Results</h2>
    <table border="1">
      <tr>
        <th>Name</th>
        <th>Marks</th>
        <th>Grade</th>
      </tr>
      <xsl:for-each select="class/student">
        <tr>
          <td><xsl:value-of select="name"/></td>
          <td><xsl:value-of select="marks"/></td>
          <td><xsl:value-of select="grade"/></td>
        </tr>
      </xsl:for-each>
    </table>
  </body></html>
</xsl:template>

Output Table

Class Results
+------------------+-------+-------+
| Name             | Marks | Grade |
+------------------+-------+-------+
| Ananya Iyer      |  88   |  A    |
| Karthik Menon    |  74   |  B    |
| Zoya Sheikh      |  95   |  A+   |
+------------------+-------+-------+

Using Position Inside a Loop

The XPath function position() returns the current loop index (starting from 1). The function last() returns the total count of nodes in the loop.

<xsl:for-each select="class/student">
  <p>
    <xsl:value-of select="position()"/>.
    <xsl:value-of select="name"/>
  </p>
</xsl:for-each>

Output

1. Ananya Iyer
2. Karthik Menon
3. Zoya Sheikh

Filtering Inside a Loop

Add an XPath predicate (a condition in square brackets) to the select expression to loop only over matching nodes.

<!-- Only process students with marks above 80 -->
<xsl:for-each select="class/student[marks > 80]">
  <p><xsl:value-of select="name"/> — <xsl:value-of select="marks"/></p>
</xsl:for-each>

Output

Ananya Iyer — 88
Zoya Sheikh — 95

Karthik Menon is excluded because his marks (74) are not above 80.

Nested Loops

A for-each can contain another for-each to handle nested XML structures.

XML Source

<company>
  <department name="Engineering">
    <employee>Rahul</employee>
    <employee>Preethi</employee>
  </department>
  <department name="HR">
    <employee>Suresh</employee>
  </department>
</company>

XSLT

<xsl:for-each select="company/department">
  <h3><xsl:value-of select="@name"/></h3>
  <ul>
    <xsl:for-each select="employee">
      <li><xsl:value-of select="."/></li>
    </xsl:for-each>
  </ul>
</xsl:for-each>

Output

Engineering
  • Rahul
  • Preethi

HR
  • Suresh

xsl:for-each vs xsl:apply-templates

Featurexsl:for-eachxsl:apply-templates
StyleInline loop — logic stays in one placeDelegates to matching templates
ReusabilityNo — template logic is inlineYes — templates are reusable
Best forSimple, self-contained transformationsComplex, modular stylesheets
ReadabilityEasy for small loopsCleaner for large stylesheets

Key Points to Remember

  • xsl:for-each loops over a set of nodes selected by the XPath in the select attribute.
  • Inside the loop, the current node changes to each matched node in turn.
  • xsl:value-of select="." outputs the text of the current node inside the loop.
  • position() returns the loop index; last() returns the total count of loop iterations.
  • XPath predicates in the select expression filter which nodes the loop processes.
  • Nested xsl:for-each handles hierarchical XML structures with multiple levels.