Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Tip: Default and error handling in XSLT lookup tables

How to deal with cases where a value isn't found in a lookup table

Uche Ogbuji (uche@ogbuji.net), Principal Consultant, Fourthought, Inc.
Photo of Uche Ogbuji
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.

Summary:  In a previous tip Uche Ogbuji demonstrated how to build lookup tables in XSLT. One follow-up question to emerge from that article is how to handle error or default conditions in XSLT lookup tables. This tip illustrates how to do so.

View more content in this series

Date:  22 Dec 2004
Level:  Intermediate

Comments:  

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.

Lookup errors

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
  


Default values

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
  


Wrap-up

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.


Resources

About the author

Photo of Uche Ogbuji

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.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=32371
ArticleTitle=Tip: Default and error handling in XSLT lookup tables
publish-date=12222004
author1-email=uche@ogbuji.net
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).