As I wrote in my earlier tip, lookup tables are a common technique for quickly finding results that would be very inefficient or impossible to determine through programming. They consist of a mapping that associates a key with a value. In that article, I demonstrated how to build lookup tables in XSLT, but I did not cover a couple of nuances of the technique: how to deal with keys not found upon lookup, or how to provide default values. You might want to review that tip before continuing with this one.
In the last article, all the state abbreviations in the source listing I provided happened to be in the lookup table. But what happens if you invoke the transform with a bogus state abbreviation, such as ZZ. The following snippet is the actual lookup code from the prior article:
<xsl:value-of select="key('state-lookup', $curr-label/address/state)/s:name"/>
|
If $curr-label were ZZ, a value not found in the state-lookup key, the result of the key function call is an empty node set, which the xsl:value-of instruction turns into an empty string. This in itself might be enough error indication for you -- or then again it may not. For example, if one of the values in the lookup table legitimately resulted in an empty string, you might not be able to distinguish the error case. The most straightforward (and unmistakable) way to signal a lookup error is to check for the empty node set result from the key function and generate an xsl:message element with the attribute terminate="yes". Listing 1 is a complete example based on the in-stylesheet lookup technique in the prior tip, updated with this error signaling technique.
Listing 1. Lookup table example that terminates on lookup error (states-lookup-error.xslt)
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://example.com/states.data"
version="1.0"
>
<xsl:output method="text"/>
<xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
<xsl:variable name="states-top" select="document('')/*/s:states"/>
<xsl:template match="label">
<xsl:value-of select="name"/>
<xsl:text> of </xsl:text>
<xsl:apply-templates select="$states-top">
<xsl:with-param name="curr-label" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="s:states">
<!-- This template updated to add a lookup error signal -->
<xsl:param name="curr-label"/>
<xsl:variable name="look-for" select="$curr-label/address/state"/>
<xsl:variable name="result"
select="key('state-lookup', $look-for)/s:name"/>
<xsl:if test="not($result)">
<xsl:message terminate="yes">
Lookup error on key: <xsl:value-of select="$look-for"/>
</xsl:message>
</xsl:if>
<!-- Push the string value of result to the output stream -->
<xsl:value-of select="$result"/>
</xsl:template>
<s:states>
<s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
<s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
<s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
<s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
</s:states>
</xsl:transform>
|
I explained the overall operation of this code in the prior tip. The only changes are to the template with match="s:states". It now uses the error signaling technique outlined above. This technique works because an XSLT key never returns an empty node set unless the lookup value was not found. Listing 2 is a mailing label file with one entry using a state abbreviation that's not in the lookup table.
Listing 2. Source file with mailing labels
<?xml version="1.0"?>
<labels>
<label>
<name>Ezra Pound</name>
<address>
<street>45 Usura Place</street>
<city>Hailey</city>
<state>ZZ</state>
</address>
</label>
<label>
<name>William Williams</name>
<address>
<street>100 Wheelbarrow Blvd</street>
<city>Patterson</city>
<state>NJ</state>
</address>
</label>
</labels>
|
Different XSLT processors signal messages with <xsl:message terminate="yes"> in different ways, but the following session shows the result in the 4XSLT processor.
$ 4xslt listing2.xml states-lookup-error.xslt
Ezra Pound of In stylesheet
file://dW/articles/xslt-lookup-defaults/states-lookup-error.xslt,
line 28, column 6:
A message instruction in the Stylesheet requested termination of processing:
Lookup error on key: ZZ
|
Rather than signal an error in the case of lookup failure, you might wish to settle on a default value. The technique for doing this is similar to signaling an error, but rather than send a terminating message you supply the default value. You might want to encode the terminating message right into the lookup table. Listing 3 is an example that supplies the value [UNKNOWN] for any key not found in the lookup table.
Listing 3. Lookup table example with default value for lookup (states-lookup-default.xslt)
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://example.com/states.data"
version="1.0"
>
<xsl:output method="text"/>
<xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
<xsl:variable name="states-top" select="document('')/*/s:states"/>
<xsl:template match="label">
<xsl:value-of select="name"/>
<xsl:text> of </xsl:text>
<xsl:apply-templates select="$states-top">
<xsl:with-param name="curr-label" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="s:states">
<!-- This template updated to add a default value signal -->
<xsl:param name="curr-label"/>
<xsl:variable name="look-for" select="$curr-label/address/state"/>
<xsl:variable name="default" select="s:default"/>
<xsl:variable name="result"
select="key('state-lookup', $look-for)/s:name"/>
<xsl:choose>
<xsl:when test="$result">
<xsl:value-of select="$result"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<s:states>
<s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
<s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
<s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
<s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
<!-- Added default value -->
<s:default><s:name>[UNKNOWN]</s:name></s:default>
</s:states>
</xsl:transform>
|
Here you still check for the empty result from the key function, but in this case you supply a default value. The value is provided in a special element right in the lookup table (s:states/s:default).
The following session shows the result in the 4XSLT processor:
$ 4xslt listing2.xml states-lookup-default.xslt Ezra Pound of [UNKNOWN] William Williams of New Jersey |
These additional refinements will help make XSLT lookup tables even more useful for you. You now have some idea of how much flexibility you will have when determining the behavior of such code, but you might also wonder about a better way to package lookup tables for reuse. This is not at all easy to do in basic XSLT 1.0, but in the future I will come back to this topic with a look at some facilities in EXSLT (see Resources) that can help make lookup tables even more handy.
- Review "XSLT lookup tables" (developerWorks, February 2001), the precursor to this tip.
- Check out the stylesheet processor that Uche used in the examples: 4XSLT is a component of the open source package 4Suite.
- Learn about EXSLT, a library of useful and widely supported extension functions for XSLT. EXSLT has some facilities that can help make managing lookup tables easier. A good place to start is "EXSLT by example" (developerWorks, February 2003).
- Find more XML resources on the developerWorks XML zone. For a complete list of XML tips to date, check out the tips summary page.
-
Browse for books on these and other technical topics.
- Find out how you can become an IBM Certified Developer in XML and related technologies.
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.

Uche Ogbuji is a consultant and co-founder of Fourthought Inc., a software vendor and consultancy specializing in XML solutions for enterprise knowledge management. Fourthought develops 4Suite, an open source platform for XML, RDF, and knowledge-management applications. Mr. Ogbuji is also a lead developer of the Versa RDF query language. He is a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can contact Mr. Ogbuji at uche@ogbuji.net.