EXSLT by example

How to put the community standard XSLT extensions to work

Community standards have had a very important role in XML technology, from SAX to RDDL. The most important community standard for XSLT is the EXSLT initiative for standard extension functions and elements. In this article, Uche Ogbuji uses practical examples to introduce and demonstrate some useful EXSLT functions.

Share:

Uche Ogbuji, 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 a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can contact Mr. Ogbuji at uche@ogbuji.net.



01 February 2003

Also available in Japanese

In a recent developerWorks column, "Extend XSLT's functionality with EXSLT," Kevin Williams wrote a high-level introduction to the community standard of extensions to XSLT. As he pointed out, the EXSLT extensions make XSLT far more useful in general-purpose data-manipulation tasks. Just to select a few random examples:

  • The EXSLT Math module contains trigonometric mathematical functions, which makes it feasible to generate pie charts in SVG.
  • The EXSLT Regular Expressions module makes it easier to parse and process user input and other variable data sources.
  • The EXSLT Dates and Times module makes it easier to render Web pages with date-sensitive content, or to process data containing date fields.

In these and many other real-life tasks, EXSLT makes using XSLT feasible in a way that is portable across the many processors that support the standard. In prior articles, I have already shown how EXSLT functions such as exsl:node-set and exsl:object-type are useful for even the most basic processing tasks. In this article I try to cover a cross-section of EXSLT's capabilities by solving a couple of simple and practical problems using EXSLT. I also try to avoid ground I've already covered to introduce the many useful EXSLT facilities that have not received the attention they deserve.

If you are completely unfamiliar with EXSLT, please read the Kevin Williams article first. The resources in that article include pointers to XSLT processors that support EXSLT, which you should install so you can play with the examples and use EXSLT yourself. Some EXSLT extensions can be used with arbitrary processors, using XSLT scripts you can download from the EXSLT site. But not all EXSLT modules are supported in this way, and this approach typically loses you a lot of performance. You may also want to read my other discussions of EXSLT on IBM developerWorks. You'll find pointers to such material in Resources.

Reach for your calendar

Problem: You generate a Web site upon user request on the server using XSLT. On that Web site you would like to display the current date and time, along with a countdown in days before a certain event.

The EXSLT Dates and Times module provides many functions for manipulating dates, including one for getting the current date, some for performing calculations between dates, and others for formatting, displaying, and interpreting dates. It also provides a <date:date-format/> extension element for customized date parsing and formatting.

Listing 1 demonstrates these facilities by displaying the current date and time, and the number of days until the start of the next month.

Listing 1. Sample code for computing date-specific details (listing1.xslt)
                <?xml version="1.0" encoding="utf-8"?> <!-- A --> <xsl:transform
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:date="http://exslt.org/dates-and-times" version="1.0" > <xsl:output
                method="html"/> <!-- B --> <xsl:variable name="now"
                select="date:date-time()"/> <xsl:template match="/"> <!-- The rest of
                the Web site HTML material would go here --> <xsl:call-template
                name="date-section"/> </xsl:template> <xsl:template
                name="date-section"> <p>This page was loaded at <xsl:text/> <!-- C
                --> <xsl:value-of select="concat(date:hour-in-day($now), ':',
                date:minute-in-hour($now), ':', date:second-in-minute($now))"/> <xsl:text>
                on </xsl:text> <xsl:value-of select="concat(date:day-in-month($now), ' ',
                date:month-name($now), ' ', date:year($now))"/> </p> <p> <!-- D
                --> <xsl:variable name="days-elapsed"
                select="concat('-P',date:day-in-month($now),'D')"/> <xsl:variable
                name="one-month-hence" select="date:add($now, 'P1M')"/> <xsl:variable
                name="next-month-start" select="date:add($one-month-hence, $days-elapsed)"/>
                <xsl:variable name="seconds" select="date:seconds( date:difference($now,
                $next-month-start) )"/> <xsl:text>The next month starts in
                </xsl:text> <xsl:value-of select="$seconds div (3600*24)"/>
                <xsl:text> days</xsl:text> </p> </xsl:template>
                </xsl:transform>

I shall discuss each of the lettered sections of the listing in turn.

A: First I declare the namespace for the EXSLT dates/times module: http://exslt.org/dates-and-times. Be careful to get this right. I've seen a few people make the mistake of using the specification page of the module, http://www.exslt.org/date/index.html, rather than the proper namespace, which results in errors.

B: The function date:date-time returns the current date and time as a string in ISO-8601 format, for example 2003-01-20T03:16:36Z.

C: Here I construct a readable representation of the date and time, using EXSLT functions that allow me to extract the various components from the date/time string. I could have done this several ways, including the date:date-format element, with which you use a format string to specify the look of the rendered date.

D: Here I use a few computing gymnastics to determine the number of days until the start of next month. I could have performed this calculation in numerous ways, some of them simpler, but I picked a sequence that shows off a good selection of EXSLT functions. First I compute the number of days elapsed in the current month as an EXSLT duration string. An EXSLT duration represents a duration of time rather than a period of time. For example, -P19D which means "a negative period of 19 days". Durations have signs for arithmetic purposes. For example, if you add the period P1D12H to a date/time representing midnight January 15, 2003, you get noon, January 15. If you add the period -P1D12H, you get noon, January 14 instead. Such calculation can be performed using the date:add function, which takes a date/time point and a duration.

Next in the listing I compute the date/time point one month from the present using date:add. In the case where $now is 2003-01-20T03:16:36Z, the result is 2003-02-20T03:16:36Z. Then I count back from that date the number of days that have elapsed, which gives me the first date of the next month -- for example, 2003-02-01T03:16:36Z. The function date:difference takes two date/time points and returns the duration between them. I use this to compute the duration until the next month and then the number of seconds in that duration -- for example, 1036800. Finally, I use integer division by (3600*24), to determine the number of days until the next month.

The output of this script is as follows:

 <p
                xmlns:date="http://exslt.org/dates-and-times">This page was loaded at 21:16:36 on
                19 January 2003</p> <p xmlns:date="http://exslt.org/dates-and-times">The
                next month starts in 12 days</p>

Note that the above output appears on a single line but has been split to multiple lines for display purposes.


Quick and dirty database operations

Problem: You have a bunch of records that you manage in an XML file, and you would like people to be able to use general queries to retrieve particular portions of this data. In other words, you want to be able to access the XML as a quick and dirty database while processing other XML data.

Take the data file in Listing 2, which represents the departments and employees in an organization.

Listing 2. A data file of employees (employees.xml)
 <?xml version="1.0"
                encoding="utf-8"?> <employees> <department title="Research">
                <employee title="Coordinator"> <name> <given>Rene</given>
                <family>Descartes</family> </name> </employee> <employee
                title="Project Manager"> <name> <given>Abu</given>
                <family>Al Kwarizmi</family> </name> </employee>
                </department> <department title="Executive"> <employee title="Chief
                Executive Officer"> <name> <given>Genghis</given>
                <family>Khan</family> </name> </employee>
                </department> <department title="Wellness"> <employee title="Manager
                of Transcendence"> <name> <given>Shakyamuni</given>
                <family>Buddha</family> </name> </employee>
                </department> </employees>

Now let's say that we want people to be able to compose memos addressed to people according to queries on this data file. Listing 3 is an example of such a memo.

Listing 3. Memo addressed according to an employee data file query (memo.xml)
                <?xml version='1.0' encoding='utf-8'?> <memo> <title>With Usura
                Hath no Man a House of Good Stone</title> <date>2003-01-14</date>
                <to> <employee-query
                query="/employees/department[@title='Executive']/employee"/> </to>
                <body> It appears the art world requires a reminder of the fact that the best
                art is created for the enjoyment of the first buyer, and not as as a mere
                investment. As I've said before, none of the work of Duccio, Piero Della Francesca,
                Pietro Lombardo, Fra Angelico, Zuan Bellini or such others would have been of any
                value if guided by usurious motives. --EP </body> </memo>

The to element contains an employee-query element, which should be replaced by the result of the query it specifies on the employee data file. Listing 4 is a small XSLT file that demonstrates how this process would work by displaying the actual names of the recipients of the memo.

Listing 4. XSLT file that displays memo recipients (listing4.xslt)
 <?xml
                version="1.0" encoding="utf-8"?> <!-- A --> <xsl:transform
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:dyn="http://exslt.org/dynamic" version="1.0" > <xsl:output
                method="text"/> <xsl:template match="/"> <!-- Jump right to the to
                element --> <xsl:apply-templates select="/memo/to"/> </xsl:template>
                <xsl:template match="to"> <xsl:apply-templates/> </xsl:template>
                <!-- B --> <xsl:template match="employee-query"> <xsl:variable
                name="db" select="document('employees.xml')"/> <xsl:variable name="query"
                select="@query"/> <xsl:for-each select="$db"> <xsl:variable
                name="recipients" select="dyn:evaluate($query)"/> <xsl:apply-templates
                select="$recipients"/> </xsl:for-each> </xsl:template>
                <xsl:template match="employee"> <xsl:value-of select="name/given"/>
                <xsl:text> </xsl:text> <xsl:value-of select="name/family"/>
                <xsl:if test="not(position()=last())"> <xsl:text>, </xsl:text>
                </xsl:if> </xsl:template> </xsl:transform>

A: Notice the different namespace that I use to set up the EXSLT module: http://exslt.org/dynamic. This module contains several functions that process strings as XPaths in real-time, and in some cases perform aggregate calculations based on such strings.

B: This template is where the real action takes place. First I read in the data file as secondary input. Then I store the query string in a variable. I have to do this because soon I'll have to shift the context so that the attribute in which this string appears is no longer readily available. The xsl:for-each element actually makes the context shift to the data file. Then dyn:evaluate function dynamically evaluates the query string against this context. The resulting node set is returned as if from a regular XPath expression evaluation. XSLT 1.0 has no way to evaluate a string dynamically as an XPath expression, but by using EXSLT I gain this capability. In the rest of the listing, conventional XSLT operations are used to print the names that come from the elements in the query result.


Summary

EXSLT includes tools for making things easier and others for making things possible in the first place. For example, the date:date-time function is impossible to replace in XSLT but dyn:evaluate() can usually be replaced by a system, whereas you use XSLT to generate another XSLT script which is what you actually run to get the desired result. EXSLT includes much more than I can hope to cover in future articles, but one nice thing about EXSLT is that it is very well documented. You'll find a world of riches to improve your XSLT development by checking out the Web site and taking advantage of this community standard.

Resources

  • The EXSLT home page is the authoritative source for information on the project. Each of the modules, elements, and functions are detailed on that site.
  • I also cover EXSLT in other articles in the developerWorks XML zone. In the "Introspector Gadget" section of "Debug XSLT on the fly" I introduce exsl:object-type and dyn:evaluate (November 2002).
  • The W3C's XSL page offers many useful links to XSLT-related resources, including the specifications themselves, tutorials, articles, and implementations.
  • Be sure to bookmark "XSL Frequently Asked Questions," which features many notes that are handy for debugging.
  • The style sheet processor used in these examples is 4XSLT, part of 4Suite, which is co-developed by the author.
  • Find other articles in Kevin William's XML for Data column.
  • Find more XML resources on the developerWorks XML zone.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
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. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

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.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

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

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=12213
ArticleTitle=EXSLT by example
publish-date=02012003