Is it possible to use a Dynamic xPath expression in a xslt style sheet?
Asked Answered
S

3

6

I'd like to use the value of an xslt parameter in an xpath expression. Specifically, as part of a not() call in an <xsl:if expression.

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- my_param contains a string '/foo/bar', passed in from ant -->
    <!-- the 'no' is just a default value -->
    <xsl:param name="my_param">no</xsl:param>
    <xsl:variable name="var_myparam" select="$my_param" />
    <!-- ... -->

    <!-- this works -->
    <xsl:if test="not(/foo/bar)" /> <!-- expression returns boolean true -->
        <!-- ... -->
    </xsl:if>

    <!-- I can't figure out how to do this the right way -->
    <!-- None of these appear to work -->
    <xsl:if test="not($var_myparam)" /> <!-- expression returns boolean false -->
        <!-- ... -->
    </xsl:if>       
    <xsl:if test="not({$var_myparam})" /> <!-- complains Required attribute 'test' is missing -->
        <!-- ... -->
    </xsl:if>       
    <xsl:if test="not({$myparam})" />   <!-- complains Required attribute 'test' is missing -->
        <!-- ... -->
    </xsl:if>       
    <xsl:if test="not($myparam)" />     <!-- expression returns boolean false -->
        <!-- ... -->
    </xsl:if>       

</xsl:transform>

I'm a little unclear on what the correct syntax is for creating a dynamic xpath expression in an xslt style sheet. I'm also a little fuzzy on the difference between a paramater, a variable, and how expanstion of both works. For example, with parameters, I know that sometimes I need the brackets, and sometimes I don't.

<!-- when creating an element, it seems I need brackets -->
<xsl:element name="{$my_param}" />

<!-- when selecting an element value for the output stream, no brackets are needed -->
<xsl:value-of select="$my_param"/>

Any general/specific help would be greatly appreciated.

Skin answered 11/10, 2009 at 19:26 Comment(1)
See also https://mcmap.net/q/1771603/-dynamic-xpath-in-xsltKamilahkamillah
K
5

Check out http://www.exslt.org/. Specifically look at the dynamic:evaluate module.

Kolva answered 11/10, 2009 at 19:43 Comment(4)
Is this possible with out of the box XSLT? It sounds like EXSLT is an extension that's not guaranteed to be part of the parser on a user's machine (I'm trying to write portable code). Is that correct?Skin
I don't believe that it is possible with XSLT "out of the box". The parameter value will be evaluated as a string, rather than a node-set. Michael Kay answered a similar question a few years back and recommended using the evaluate() function as a solution. biglist.com/lists/xsl-list/archives/200406/msg00006.htmlUnaccountable
Michael Kay? What does he know about it :) Sounds like it's EXSLT or nothing then, which means you're SOL unless you're using one of the blessed parsers.Skin
Alan, you are exactly right, compatibility is not guaranteed. I've been looking for a list of the 'blessed parsers' and have come up empty. I know from use (and the EXSLT documentation) that Apache servers are fine, and from what I've seen by searching, it's been suggested that MSXML would work as well.Kolva
D
4

XSLT doesn't support dynamic evaluation of XPath - that's why you need to use an extension function like EXSLT's evaluate to do it.

If you don't want to use extension functions (and there are good reasons not to), another possibility is to query the input document's DOM before you execute the transform, and pass the node-set into the transform as a parameter.

Edit

About those curly braces: Those are attribute value templates (AVTs). These are semantically equivalent in an XSLT transform:

<foo>
   <xsl:attribute name="bar">
      <xsl:value-of select="XPathExpression"/>
   </xsl:attribute>
</foo>

<foo bar="{XPathExpression}"/>

The second is just a shortcut for the first.

About variables and parameters: Syntactically, there's no difference between a variable and a parameter; anywhere you reference $foo in an XPath expression it will work the same whether foo is defined by xsl:variable or xsl:param.

The difference between the two is how they get populated. Variables are populated in the xsl:variable declaration. Parameters are declared in a named template using xsl:param, but they're populated by whatever calls the named template, using xsl:with-param, e.g.:

<xsl:call-template name="foo">
   <xsl:with-param name="bar" select="XPathExpression"/>
</xsl:call-template>

<xsl:template name="foo">
   <xsl:param name="bar"/>
   ...

The big honking exception to this is parameters that are a child of xsl:stylesheet. There's no way to populate those parameters within the transform; those are populated externally, by whatever (environment-specific) mechanism is invoking the transform.

A pretty common use case is making up for the fact that XSLT doesn't have a system date function. So you'll see something like:

<xsl:stylesheet ...
   <xsl:param name="system-date"/>
   ...

and then, when invoking the transform, something like this (in C#):

XsltArgumentList args = new XsltArgumentList();
args.AddParam("system-date", "", DateTime.Now.ToString("s"));
xslt.Transform(input, args, result);
Degeneracy answered 12/10, 2009 at 17:45 Comment(3)
Yep, that seems to be the case. It was hard to find someone that would just come out and say it (most articles have a "look what you can do" point of a view, not a "look what you can't). Regarding your suggestion, what tools would let you pass a node-set into an xslt document as a parameter for the transformation? I don't think that's possible with ant, and I'd be curious to see the semantics of tools that do support it.Skin
How passing a parameter into a transform works depends on the XSLT processor you're using. For instance, in C# you'd use XmlNode.Select() to build a node-set (i.e. XmlNodeList). Then you'd create an XmlArgumentList object, populate it with the node-set, and pass it into XslCompiledTransform.Transform(). If you're using a tool that doesn't give you programmatic access to the input document's DOM, that avenue's probably open to you.Degeneracy
Does anyone know if there's a way to pass the argument nodes in Java with the built-in XSLT?Philanthropist
E
3

Try some variation of this:

<xsl:if test="not($var_myparam = 'no'))">
</xsl:if>

The problem is in how XPath evaluates booleans. Any non-empty string will evaluate to true in XPath. Checkout this write-up about xpath booleans.

As for your other questions... Variables and parameters act the same as each other in XPath expressions. Both are referenced with the form $var.

For any XSLT attribute named "select," you don't need the brackets. The brackets are used in what are called "attribute value templates." You use them when XSLT is expecting a string, for example:

<xsl:template match="name-a">
  <xsl:variable name="old-name" select="name(.)" />
  <name-b old-name="{$old-name}" new-attribute="hello"  />
</xsl:template>

The XSLT spec talks about AVTs, and so does this page.

Enviable answered 11/10, 2009 at 19:40 Comment(2)
Useful info, thanks. Slight mis-communication on my part though. The parameter that's being passed in is a string in the form '/foo/bar'. The string 'no' is just a default. At the point of my if statment, I have '/foo/bar' in a parameter. I want to use that string as an xpath expression to check. Is that possible?Skin
Hmm, in that case you'll have to use an extension function, per the discussion in CNutt's answer. saxon:eval() is an option: saxonica.com/documentation/extensions/functions/evaluate.htmlEnviable

© 2022 - 2024 — McMap. All rights reserved.