XSLT Tips

[MSXML3] Dynamic XPATHs

Let's say you want to generate an XPATH using a variable or a value derived from the document itself.  The variable contains the name of an attribute to access.

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

<msxsl:script implements-prefix="dyn" language="jscript">
   function evaluate(context, expression)
      {
         return context.nextNode().selectNodes(expression);
      }
</msxsl:script>

...

</xsl:stylesheet>


You could call it with:

   <xsl:value-of select="dyn:evaluate(., concat('@', $y))"/>

to get the attribute named the same as the string held in $y on the context node.

I've used the EXSLT namespace here, but you should make up your own.

<Opinion>
What the hell was the XSLT 1.0 spec team thinking?  Why isn't this part of the spec?
</Opinion>


[MSXML3] Dynamic XPATHs II

Let's try a more advanced example, one that needs to put quotes around a string.  Quotes are a challenge in XPATH statements because you can't put &apos; in an XPATH expression.  It simply doesn't work because the entity gets resolved when the stylesheet is parsed.  My solution is to write an extension function that returns a single quote and use 'concat'.

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
extension-element-prefixes="dyn msxsl">
   <xsl:output method="html" />

   <msxsl:script implements-prefix="dyn" language="jscript">
   
   function evaluate(context, expression) 
   { return context.nextNode().selectNodes(expression); } 
   
   function quote() { return "'"; }
   
   </msxsl:script>

   <xsl:template match="/">
      <html>
         <body>
            <xsl:apply-templates />
         </body>
      </html>
   </xsl:template>

   <xsl:template match="Instances/Instance">
      <xsl:if test="@name">
         <xsl:choose>
            <xsl:when test="dyn:evaluate(., concat('//Step[@*=', 
            dyn:quote(), @name, dyn:quote(), ']'))" />

            <xsl:otherwise>
               <xsl:value-of select="@name">
               </xsl:value-of>

               <br />
            </xsl:otherwise>
         </xsl:choose>
      </xsl:if>
   </xsl:template>
</xsl:stylesheet>

This example creates XPATH statements that look like:

//Step[@*='foo']

Where the string foo comes from an attribute in the document.


Extracting the Last Child from Its Parent and Ancestors

Let's say you want to produce <child id='b'/> from the following:

<Root>
  <parent>
    <child id='a'/>
    <child id='b'/>
  </parent>
</Root>

Here's how to do it:

<xsl:template match="parent/child[last()]">
<xsl:copy>
  <xsl:copy-of select="@* | * | text()" />
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>


(c) 2008 http://www.angelfire.com/nv/fast/

Xslt Tips