Translate

Archives

XSLT Dynamic Path Evaluation

One of the problems that beginning users of the XSLT language who come from more traditional languages such as C may encounter is the question of how to evaluate an XPath expression that is build up from one or more strings such as in the following simple example:

<xsl:variable name="xPath:">/root/first</xsl:variable>

<xsl:variable name="xNodeSet">
    <xsl:copy-of select='$xPath' />
</xsl:variable>


To the surprise of all who encounter this issue for the first time, xPath will be evaluated as a string, rather than as a node-set and the variable XNodeSet is set to the string /root/first. This is because the Xpath expression is dynamic rather than static and Section 2.1.2 of XSLT 2.0 states that:

The dynamic context of an expression is defined as information that is available at the time the expression is evaluated. If evaluation of an expression relies on some part of the dynamic context that has not been assigned a value, a dynamic error is raised [err:XPDY0002].

Thus there is no support for dynamic XPath expression evaluation in XSLT1 or XSLT2 unless your processor provides an extension function or you use an extension library. For example, the Saxon and the Xalan processors have the evaluate function and the EXSLT extension library has the dyn:evaluate function.

Consider the following XML document. It is similar to the output of the Unix ls command.

<files>
    <file>
         <name>example1</name>
         <size>1035</size>
         <uid>655</uid>
         <gid>110</gid>
         <atime>
              <hour>12</hour>
              <minute>41</minute>
             <second>49</second>
             <month>05</month>
             <day>04</day>
             <year>2008</year>
        </atime>
        <ctime>
              <hour>13</hour>
              <minute>43</minute>
              <second>41</second>
              <month>05</month>
              <day>06</day>
              <year>2008</year>
         </ctime>
         <mtime>
               <hour>16</hour>
              <minute>27</minute>
              <second>29</second>
              <month>04</month>
              <day>26</day>
              <year>2008</year>
         </mtime>
     </file>
     <file>
          <name>example2</name>
          <size>1585</size>
          <uid>655</uid>
          <gid>110</gid>
          <atime>
                <hour>16</hour>
                <minute>11</minute>
                <second>19</second>
                <month>05</month>
                <day>04</day>
                <year>2008</year>
           </atime>
           <ctime>
                <hour>11</hour>
                <minute>37</minute>
                <second>09</second>
                <month>05</month>
                <day>16</day>
                <year>2008</year>
           </ctime>
           <mtime>
                 <hour>23</hour>
                 <minute>31</minute>
                 <second>22</second>
                 <month>05</month>
                 <day>10</day>
                 <year>2008</year>
           </mtime>
      </file>
</files>


Suppose we want to transform this document into a more compact form such as:

<?xml version="1.0"?>
<files>
     <file uid="655" gid="110" size="1035" atime="2008/05/04 12:41:49" ctime="2008/05/06 13:43:41" mtime="2008/04/26 16:27:29">example1</file>
     <file uid="655" gid="110" size="1585" atime="2008/05/04 16:11:19" ctime="2008/05/16 11:37:09" mtime="2008/05/10 23:31:22">example2</file>
</files>

Here is one way to do the transformation using dynamic XPath expression evaluation and the EXSLT dyn:evaluate function in the Date template.

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:dyn="http://exslt.org/dynamic"
   extension-element-prefixes="dyn">

   <xsl:output method="xml" indent="yes" />

   <xsl:template match="/">
      <xsl:element name="files">
          <xsl:apply-templates select="//file"/>
      </xsl:element>
   </xsl:template>

   <xsl:template name="Date">
       <xsl:param name="str" />
            <xsl:value-of select="dyn:evaluate($str)/year" />/<xsl:value-of select="dyn:evaluate($str)/month" />/<xsl:value-of select="dyn:evaluate($str)/day" /><xsl:text> </xsl:text><xsl:value-of select="dyn:evaluate($str)/hour" />:<xsl:value-of select="dyn:evaluate($str)/minute" />:<xsl:value-of select="dyn:evaluate($str)/second" />
   </xsl:template>

   <xsl:template match="file">
       <xsl:element name="file">
            <xsl:attribute name="uid">
                <xsl:value-of select="uid" />
            </xsl:attribute>
            <xsl:attribute name="gid">
                <xsl:value-of select="gid" />
            </xsl:attribute>
            <xsl:attribute name="size">
                <xsl:value-of select="size" />
            </xsl:attribute>
            <xsl:attribute name="atime">
                <xsl:call-template name="Date">
                    <xsl:with-param name="str">./atime</xsl:with-param>
                </xsl:call-template>
            </xsl:attribute>
            <xsl:attribute name="ctime">
                <xsl:call-template name="Date">
                    <xsl:with-param name="str">./ctime</xsl:with-param>
                </xsl:call-template>
            </xsl:attribute>
            <xsl:attribute name="mtime">
                <xsl:call-template name="Date">
                    <xsl:with-param name="str">./mtime</xsl:with-param>
                </xsl:call-template>
            </xsl:attribute>
            <xsl:value-of select="name" />
       </xsl:element>
   </xsl:template>

</xsl:stylesheet>


If you don’t want to use extension functions (possibility for portability reasons), another method might be to query the input document’s DOM using a custom application before you execute the transformation, and pass the node-set into the processor as a parameter.

The XSLT 2.0 working group considered adding support for dynamic XPath expression evaluation but decided not to pursue it because, according to the working group, dynamic evaluation

has significant implications on the runtime architecture of the processor as well as the ability to do static optimization

Unfortunately, once again, that left a significant and tremendously useful feature on the standards sidelines one again.

Update June 2010: There are plans to incorporate dynamic XPath evaluation in XSLT 2.1, the first draft of which was published in May 2010.

Comments are closed.