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: XSLT lookup tables

Uche Ogbuji (uche@ogbuji.net), CEO and principal consultant, Fourthought, Inc.
Uche Ogbuji is a consultant and co-founder of Fourthought Inc., a consulting firm specializing in XML solutions for enterprise knowledge management applications. Fourthought develops 4Suite, the open-source platform for XML middleware. Mr. Ogbuji is a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. He can be reached at uche@ogbuji.net.

Summary:  Lookup tables, a common programming tool, efficiently transform one value to another. In this tip learn how to implement lookup tables in XSLT. A basic understanding of XSLT is required.

Date:  01 Feb 2001
Level:  Introductory

Comments:  

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. Lookup tables can be just as useful in XSLT as in any other language, especially since XSLT is highly specialized for transforming XML trees, and not very powerful at string or numeric manipulation.

This tip reveals how to use lookup tables in XSLT and how to package tables conveniently within a style sheet.

A sample problem

Let's say you have a source document representing a list of mailing labels, as in Listing 1.

<?xml version="1.0"?>
<labels>
 <label>
  <name>Thomas Eliot</name>
  <address>
   <street>3 Prufrock Lane</street>
   <city>Hartford</city>
   <state>CT</state>
  </address>
 </label>
 <label>
  <name>Ezra Pound</name>
  <address>
   <street>45 Usura Place</street>
   <city>Hailey</city>
   <state>ID</state>
  </address>
 </label>
 <label>
  <name>William Williams</name>
  <address>
   <street>100 Wheelbarrow Blvd</street>
   <city>Patterson</city>
   <state>NJ</state>
  </address>
 </label>
</labels>

. . . and you want to generate from this a summary of names and states of residence, as in Listing 2:

Thomas Eliot of Connecticut
Ezra Pound of Idaho
William Williams of New Jersey

You have to find a way to map the state abbreviations, such as "ID" to the full name, "Idaho". Of course, this would have been quite a simple matter if the authors of the source document had used the full names of the states, but you cannot always control the source document format.


Lookup table in a separate document

One solution is to create a lookup table document. In this case, it's as simple as the lookup table in Listing 3:

<?xml version="1.0"?>
<states>
 <state><abbr>CO</abbr><name>Colorado</name></state>
 <!-- etc. -->
 <state><abbr>CT</abbr><name>Connecticut</name></state>
 <!-- etc. -->
 <state><abbr>ID</abbr><name>Idaho</name></state>
 <!-- etc. -->
 <state><abbr>NJ</abbr><name>New Jersey</name></state>
 <!-- etc. -->
</states>

Not all the states are listed, but you get the idea. The following is a style sheet that renders our desired results using the lookup table in states.xml. Note that I have annotated the transform with letters in comments to mark places I'll explain in detail.

<?xml version="1.0"?>
<xsl:transform
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">
 <xsl:output method="text"/>
 
<!-- A -->
 <xsl:key name="state-lookup" match="state" use="abbr"/>
 
<!-- B -->
 <xsl:variable name="states-top" select="document('states.xml')/states"/>
 <xsl:template match="label">
  <xsl:value-of select="name"/>
  <xsl:text> of </xsl:text>
  
<!-- C -->
  <xsl:apply-templates select="$states-top">
   <xsl:with-param name="curr-label" select="."/>
  </xsl:apply-templates>
 </xsl:template>
 
<!-- D -->
 <xsl:template match="states">
  <xsl:param name="curr-label"/>
  <xsl:value-of select="key('state-lookup', $curr-label/address/state)/name"/>
 </xsl:template>
</xsl:transform>

A. First define a key that represents the lookup table in the style sheet. Remember that XSLT keys will create a named index of nodes in source documents which are selected by the match attribute. The key is computed from the use attribute. An essential point is that a separate index is prepared for each source document that is loaded (using the document() function). When the key is queried using the key() function, it uses the index of the source document that contains the context node.

B. This variable is set to the document element (states) of the states.xml document, and this automatically triggers the indexing of that document when it is loaded. Note that the original source document -- with the labels themselves -- does not have any nodes that match the state-lookup key definition, so its index is empty.

C. At this point you've rendered the name on the label and the text "of". Next you have to put the lookup table to use and render the state name. There's a seemingly obvious solution:

  <xsl:value-of select="key('state-lookup', address/state)/name"/>

But the obvious solution would get you nowhere because, as I already mentioned, when a key is invoked, it searches the index on the document containing the context node. Currently the context is a label element on the original source document, which has an empty state-lookup index. So you need to shift the context to the states.xml document, which does have the needed entries in its index.

The most straightforward way to do this is using xsl:apply-templates to select a node in that document. The value of the states-top variable will do just fine. However, you also have to keep track of the current context node so that you can look up the relevant state abbreviation. You solve this by passing the current node as a parameter to the next template.

D. Now it's a simple matter of invoking the key to render the output you want. You look up by the string value of the state child of the address child of the label node passed in as a parameter. The result of the key lookup is a node set containing one of the state elements in the states.xml document. Finally, select the name child of this element, which contains the full state name you are looking for.

Try it yourself. You should get the results I promised earlier:

[uogbuji@borgia code]$ 4xslt labels.xml state-summary.xslt
 Thomas Eliot of Connecticut
 Ezra Pound of Idaho
 William Williams of New Jersey
[uogbuji@borgia code]$ 


Lookup table from the transform itself

Keeping the lookup table solves the problem, but it might be inconvenient to have to always provide two documents: the XSLT transform and the lookup table document. Luckily, using XML namespaces, it's easy enough to include the lookup table right within the style sheet itself.

<?xml version="1.0"?>
<!-- E -->
 <xsl:transform
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:s="http://states.data"
 version="1.0">
 <xsl:output method="text"/>
 
<!-- F -->
 <xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
 
<!-- G -->
 <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>
 
<!-- H -->
 <xsl:template match="s:states">
  <xsl:param name="curr-label"/>
  <xsl:value-of select="key('state-lookup', $curr-label/address/state)/s:name"/>
 </xsl:template>
 
<!-- I -->
 <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>

E. You will need to set aside a namespace for the embedded lookup table or the XSLT processor will signal an error when it encounters the lookup table elements. XSLT processors must ignore top-level elements in a foreign namespace, which is a fact you turn to your favor.

F. Now you have to use the namespace prefix for the lookup table elements.

G. Use the special rule that if document() is passed an empty string, it returns the style sheet itself as a source document. Now you have to use the namespace prefix for the lookup table elements. This will cause the style sheet itself to be loaded, and the embedded lookup elements to be indexed. Also note that the s:states element is no longer the document element. Instead it is the child of xsl:transform. You have changed the variable's select to accommodate this difference.

H. Same comment as F.

I. Here is the embedded lookup table. Nothing new here except that all elements are now in a namespace, and that s:states is no longer the document element.


Conclusion

The second solution is convenient and elegant. All the user needs is the style sheet, and there is no need to fiddle with separate documents just to look up values.

This technique is useful in a variety of ways. A more involved lookup table than the one presented here could be used to convert a date such as "1/1/2001" to "January 1, 2001".

Sometimes it is useful to have a lookup table in a separate document. For instance, an application could convert currency values in an XML document from dollars to pounds sterling using a lookup table that is retrieved from a query to an XML-enabled database.

Hopefully you find the technique useful, and you have learned something from the insights it gives into the workings of XSLT.


Resources

  • The W3C's XSL page with many useful links to XSLT-related resources, including the specifications themselves, tutorials, articles, and implementations.

  • The style sheet processor I used in the examples is 4XSLT, 4Suite.

About the author

Uche Ogbuji

Uche Ogbuji is a consultant and co-founder of Fourthought Inc., a consulting firm specializing in XML solutions for enterprise knowledge management applications. Fourthought develops 4Suite, the open-source platform for XML middleware. Mr. Ogbuji is a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. He can be reached 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=11966
ArticleTitle=Tip: XSLT lookup tables
publish-date=02012001
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).