XSLT Conditional Logic: if, choose, when and otherwise

In XSLT (Extensible Stylesheet Language Transformations), we often need to produce different output based on conditions. XSLT gives us two main instructions for this. The xsl:if instruction and the xsl:choose instruction. In XSLT 2.0 or later, we also have a shorter inline syntax. I thought of writing a blog post so others can easily find examples as well.

xsl:if Instruction

xsl:if runs its content only when the test expression is true. Please note there is no "else" part in xsl:if. If you need an else branch, use xsl:choose instead.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="text"/>
    <xsl:template match="order">
        <xsl:if test="total &gt; 1000">
            <xsl:value-of select="'High value order'"/><!-- Prints only when total is above 1000 -->
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Common test expressions:

<xsl:if test="status = 'ACTIVE'"><!-- String comparison --></xsl:if>
<xsl:if test="total &gt;= 100"><!-- Number comparison. Use &gt; and &lt; for > and < --></xsl:if>
<xsl:if test="discount"><!-- True when the discount element exists --></xsl:if>
<xsl:if test="not(discount)"><!-- True when the discount element does not exist --></xsl:if>
<xsl:if test="items/item"><!-- True when at least one item exists --></xsl:if>
<xsl:if test="email != ''"><!-- True when email is not empty --></xsl:if>
<xsl:if test="contains(email, '@example.com')"><!-- String contains check --></xsl:if>

We can combine conditions with and and or.

<xsl:if test="status = 'ACTIVE' and total &gt; 1000">
    <xsl:value-of select="'Active high value order'"/>
</xsl:if>

<xsl:if test="country = 'LK' or country = 'AU'">
    <xsl:value-of select="'Supported region'"/>
</xsl:if>

The xsl:choose Instruction

xsl:choose is the if else and switch of XSLT. It contains one or more xsl:when branches and an optional xsl:otherwise branch. The first matching xsl:when wins. The order of the branches matters.

<xsl:template match="order">
    <xsl:choose>
        <xsl:when test="total &gt; 5000">
            <xsl:value-of select="'Platinum'"/>
        </xsl:when>
        <xsl:when test="total &gt; 1000">
            <xsl:value-of select="'Gold'"/>
        </xsl:when>
        <xsl:when test="total &gt; 100">
            <xsl:value-of select="'Silver'"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="'Standard'"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Please note an order with total 6000 matches the first branch only. XSLT stops checking after the first match. So always put the most specific condition first.

Setting a Variable with a Condition

A very common use case is assigning a variable based on a condition. We wrap xsl:choose inside xsl:variable.

<xsl:variable name="shippingCost">
    <xsl:choose>
        <xsl:when test="total &gt; 1000">
            <xsl:value-of select="0"/><!-- Free shipping -->
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="25"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>
<xsl:value-of select="$shippingCost"/>

Inline if then else in XSLT 2.0

In XSLT 2.0 or later, XPath supports if then else directly inside expressions. This is much shorter for simple cases.

<xsl:value-of select="if (total &gt; 1000) then 'High' else 'Normal'"/>

<xsl:variable name="shippingCost" select="if (total &gt; 1000) then 0 else 25"/>

Please note the else part is mandatory in the inline syntax. We cannot skip it like xsl:if.

In XSLT 3.0 with XPath 3.1, there is also an even shorter otherwise style using let expressions. However, the if then else form above covers most needs.

Conditions in Match Patterns

Sometimes we do not need xsl:if at all. We can put the condition inside the template match pattern. This is the more natural XSLT style.

<xsl:template match="order[total &gt; 1000]">
    <xsl:value-of select="'High value order'"/>
</xsl:template>

<xsl:template match="order">
    <xsl:value-of select="'Normal order'"/>
</xsl:template>

The processor picks the more specific pattern automatically. If both patterns have the same specificity, set the priority attribute to control which one wins.

Conditions in Select Expressions

We can also filter inside select expressions using predicates. This avoids conditional instructions completely.

<xsl:for-each select="order[status = 'ACTIVE']"><!-- Loops only active orders -->
    <xsl:value-of select="id"/>
</xsl:for-each>

<xsl:value-of select="count(order[total &gt; 1000])"/><!-- Counts high value orders -->

Checking for Empty or Missing Values

This part confuses many people. These three tests mean different things.

<xsl:if test="discount"><!-- Element exists. Even when empty. --></xsl:if>
<xsl:if test="discount != ''"><!-- Element exists and has a value --></xsl:if>
<xsl:if test="string-length(normalize-space(discount)) &gt; 0"><!-- Has a real value. Spaces do not count. --></xsl:if>

The third form is the safest when the input can contain whitespace only values.

Complete Example

Here is a complete stylesheet which grades students based on marks.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/students">
        <results>
            <xsl:for-each select="student">
                <result name="{name}">
                    <xsl:choose>
                        <xsl:when test="marks &gt;= 75">
                            <xsl:attribute name="grade">A</xsl:attribute>
                        </xsl:when>
                        <xsl:when test="marks &gt;= 65">
                            <xsl:attribute name="grade">B</xsl:attribute>
                        </xsl:when>
                        <xsl:when test="marks &gt;= 50">
                            <xsl:attribute name="grade">C</xsl:attribute>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:attribute name="grade">F</xsl:attribute>
                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:if test="marks &gt;= 90">
                        <xsl:attribute name="honors">true</xsl:attribute>
                    </xsl:if>
                </result>
            </xsl:for-each>
        </results>
    </xsl:template>
</xsl:stylesheet>

You can use an online XSLT playground such as xslttest or XSLT Tester VS Code Plugin to verify the examples listed above. Please refrain from copying content to other websites

No comments:

Post a Comment


View Counts

Followers