Improving Performance with Keys

Contact Us or call 1-877-932-8228
Improving Performance with Keys

Improving Performance with Keys

The major benefit of using keys is that the XSLT processor creates an index of the matched nodes, which allows it to access those nodes from anywhere without searching through the whole node tree.

Cross References

Keys are commonly used for cross referencing. To illustrate, we'll first study an example of a transformation that doesn't use keys. Take a look at the following XML document, which contains a list of U.S. states and their abbreviations followed by a list of cities with their states and populations.

Code Sample:

Keys/Demos/CitiesWithoutKey.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="CitiesWithOutKey.xsl"?>
<CityStates>
	<States>
		<State Abbr="AL">Alabama</State>
		<State Abbr="AK">Alaska</State>
		<State Abbr="AZ">Arizona</State>
		<State Abbr="AR">Arkansas</State>
		<State Abbr="CA">California</State>
		<State Abbr="CO">Colorado</State>
		<State Abbr="CT">Connecticut</State>
		<State Abbr="DE">Delaware</State>
		<State Abbr="FL">Florida</State>
---- C O D E   O M I T T E D ----

	</States>
	<Cities Source="http://www.city-data.com/top1.html">
		<City State="NY" Population="8084316">New York</City>
		<City State="CA" Population="3798981">Los Angeles</City>
		<City State="IL" Population="2886251">Chicago</City>
		<City State="TX" Population="2009834">Houston</City>
		<City State="PA" Population="1492231">Philadelphia</City>
		<City State="AZ" Population="1371960">Phoenix</City>
		<City State="CA" Population="1259532">San Diego</City>
		<City State="TX" Population="1211467">Dallas</City>
		<City State="TX" Population="1194222">San Antonio</City>
---- C O D E   O M I T T E D ----

	</Cities>
</CityStates>

Our goal is to take this document and output an HTML page that looks like this:

To do this, we'll loop through the City elements outputting a list item for each one. But the City elements just contain the state abbreviation and we want to output the full state name. To do this, we'll need to look up the state name in the State elements. The following code sample shows how to do this without keys.

Code Sample:

Keys/Demos/CitiesWithoutKey.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<html>
			<head>
				<title>Largest Cities in the United States</title>
			</head>
			<body>
			<h1>Largest Cities in the United States</h1>
			<ol>
				<xsl:for-each select="//City">
					<xsl:variable name="State" select="@State"/>
					<li>
						<xsl:value-of select="."/>
						<xsl:text> is in the state of </xsl:text>
						<xsl:value-of select="//States/State[@Abbr=$State]"/>
						<xsl:text> and has a population of </xsl:text>
						<xsl:value-of select="format-number(@Population,'#,###')"/>
						<xsl:text>.</xsl:text>
					</li>
				</xsl:for-each>
			</ol>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>

As suggested above, we loop through the City elements outputting list items. With each iteration, the context node is a different City element, which contains the city's name and population, so we can output those directly. The City element also contains the state abbreviation. The trick is to use that to get the full name of the state. This XPath does the trick: //States/State[@Abbr=$State]. It goes back up to the root of the document and looks through the State elements for one that has an abbreviation matching the State attribute (held in a variable) of the City element.

Although this works, it is an intensive process. For each City, the XSLT processor has to make a trip through the node tree looking for a matching State.

The Key Way

Let's take a look at another code sample that uses keys to accomplish the same thing.

Code Sample:

Keys/Demos/CitiesWithKey.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:key name="keyState" match="State" use="@Abbr"/>
	<xsl:template match="/">
		<html>
			<head>
				<title>Largest Cities in the United States</title>
			</head>
			<body>
			<h1>Largest Cities in the United States</h1>
			<ol>
				<xsl:for-each select="//City">
					<li>
						<xsl:value-of select="."/>
						<xsl:text> is in the state of </xsl:text>
						<xsl:value-of select="key('keyState',@State)"/>
						<xsl:text> and has a population of </xsl:text>
						<xsl:value-of select="format-number(@Population,'#,###')"/>
						<xsl:text>.</xsl:text>
					</li>
				</xsl:for-each>
			</ol>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>

In the example above, the key is created with this line:

<xsl:key name="keyState" match="State" use="@Abbr"/>

Imagine that it creates a lookup table that looks something like this:

Index Node-set
AL Alabama State node
AK Alaska State node
AZ Arizona State node
AR Arkansas State node
CA California State node

The key() function is used to look up the nodes specified by the key.

<xsl:value-of select="key('keyState',@State)"/>

If the value of the State attribute in the current City element is found in the lookup table created by the keyState key, then that value is output.

Again, the major reason this is better than the first way is that the XSLT processor creates an index of the nodes, so that it does not have to search through the whole node tree looking for a match.

Next