Skip to main content

skip to main content

developerWorks  >  XML  >

EXSLT by example

How to put the community standard XSLT extensions to work

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Intermediate

Uche Ogbuji (uche@ogbuji.net), Principal Consultant, Fourthought, Inc.

01 Feb 2003

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.

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.



Back to top


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.



Back to top


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



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




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top