Recipe1.4.Shrinking Conditional Code with If Expressions


Recipe 1.4. Shrinking Conditional Code with If Expressions

Problem

Your complex XSLT code is too verbose due to the high overhead of XML when expressing simple if-then-else conditions.

Solution

XPath 1.0

There are a few tricks you can play in XPath 1.0 to avoid using XSLT's verbose xsl:choose in simple situations. These tricks rely on the fact that false converts to 0 and true to 1 when used in a mathematical context.

So, for example, min, max, and absolute value can be calculated directly in XPath 1.0. In these examples, assume $x and $y contain integers.

(: min :) ($x <= $y) * $x + ($y < $x) * $y (: max :) ($x >= $y) * $x + ($y > $x) * $y (: abs :) (1 - 2 * ($x < 0)) * $x

XPath 2.0

For the simple cases in the XPath 1.0 section (min, max, abs), there are now built-in XPath functions. For other simple conditionals, use the new conditional if expression.

(: Default the value of a missing attribute to 10. :) if (@x) then @x else 10 (: Default the value of a missing element to 'unauthorized'. :) if (password) then password else 'unauthorized''unauthorized' (: Guard against division by zero. :) if ($d ne 0) then $x div $d else 0 (: A para elements text if it contains at least one non-whitespace character; otherwise, a single space. :) if (normalize-space(para)) then string(para) else ' '

Discussion

If you are a veteran XSLT 1.0 programmer, you probably cringe every time you need to add some conditional code to a template. I know I do, and often go through pains to exploit XSLT's pattern-matching constructs to minimize conditional code. This is not because such code is more complicated or inefficient in XSLT but rather because it is so darn verbose. A simple xsl:if is not that bad, but if you need to express if-then-else logic, you are now forced to use the bulkier xsl:choose. Yech!

In XSLT 2.0, there is an alternative but it is delivered in XPath 2.0 rather than XSLT 2.0 proper. On first exposure, one may get the impression that XPath was somehow bastardized via the introduction of what procedural programmers call flow of control statements. However, once you begin to use XPath 2.0 in its full glory, you should quickly conclude that both XPath and XSLT is bettered by these enhancements. Further, the XPath 2.0 conditional expression does not deprecate the xsl:if element but rather reduces the need to use it in just those cases where it is most awkward. As an illustration, compare the following snippets:

<!-- XSLT 1.0 --> <xsl:variable name="size">   <xsl:choose>      <xsl:when test="$x &gt; 3">big</xsl:when>     <xsl:otherwise>small</xsl:when>   </xsl:choose> </xsl:variable> <!-- XSLT 2.0 --> <xsl:variable name="size" select="if ($x gt 3) then 'big' else 'small' "/>

I think most readers will prefer the later XPath 2.0 solution over the former XSLT 1.0 one.

One important fact about the XPath conditional expression is that the else is not optional. C programmers can appreciate this by comparing it to the a ? b : c expression in that language. Often one will use the empty sequence ( ) when there is no other sensible value for the else part of the expression.

Conditional expressions are useful for defaulting in the absence of a schema that provides defaults.

(: Defaulting the value of an optional attribute :) if (@optional) then @optional else 'some-default´ (: Defaulting the value of an optional element :) if (optional) then optional else 'some-default´

Handling undefined or undesirable results in expressions is also a good application. In this example we have an application specific reason to prefer 0 rather than number(`Infinity') as the result.

if ($divisor ne 0) then $dividend div $divisor else 0

You can also create conditions that are more complex. The following code that decodes an enumerated list

if (size eq 'XXL') then 50 else if (size eq 'XL') then 45 else if (size eq 'L') then 40 else if (size eq 'M') then 34 else if (size eq 'S') then 32 else if (size eq 'XS') then 29 else -1

However, in this case, you might find a solution using sequences to be cleaner especially if you replace the literal sequences with variables that might be initialized from an external XML file.

(50,45,40,34,32,29,-1)[(index-of((('XXL', 'XL', 'L', 'M', 'S', 'XS')), size), 7)[1]]

Here we are assuming the context has only a single size child element otherwise the expression is illegal (but you can then write size[1] instead). We are also relying on the fact that index-of returns an empty sequence when the search item is not found which we concatenate with 7 to handle the else case.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net