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
| Feature | xsl:for-each | xsl:apply-templates |
|---|---|---|
| Style | Inline loop — logic stays in one place | Delegates to matching templates |
| Reusability | No — template logic is inline | Yes — templates are reusable |
| Best for | Simple, self-contained transformations | Complex, modular stylesheets |
| Readability | Easy for small loops | Cleaner 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.
