Yes, we can ... at least rudimentarily. Here is a workaround that I use with Saxon CE (XSLT 2.0) until the evaluate function is available. This may not work for all kinds of complex XML documents, but you can probably tweak the βfilterβ as needed (request for attributes, etc.).
In my special situation, I have xPath expressions describing the "full" path to elements, including their names, and the trick is to use a template in combination with only the last element of the xPath dynamic expression, for example. use "third" instead of "first / second / third":
<xsl:variable name="value" select="//*[name() = 'third']" />
To limit the result (we select all elements with the name "third"), you will also have to query the ancestors "first" and "second". Maybe someone has an idea to simplify the following code, in particular the ancestors call:
<xsl:variable name="record" select="document('record.xml')/data"/> <xsl:function name="utils:evaluateXPath"> <xsl:param name="xpath" as="xs:string"/> <xsl:choose> <xsl:when test="function-available('evaluate')"> <xsl:value-of select="'function evaluate() can be used ...'"/> </xsl:when> <xsl:otherwise> <xsl:variable name="sequence" select="tokenize($xpath, '/')" /> <xsl:variable name="iAncestors" select="count($sequence)-1" as="xs:integer" /> <xsl:variable name="lastElement" select="if ($iAncestors > 0) then $sequence[last()] else $xpath" /> <xsl:value-of select=" if ($iAncestors = 0) then ($record//*[name() = $lastElement and not(*)])[1] else if ($iAncestors = 1) then ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[1])])[1] else if ($iAncestors = 2) then ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[2]) and (name(../..) = $sequence[1])])[1] else if ($iAncestors = 3) then ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[3]) and (name(../..) = $sequence[2]) and (name(../../..) = $sequence[1])])[1] else if ($iAncestors = 4) then ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[4]) and (name(../..) = $sequence[3]) and (name(../../..) = $sequence[2]) and (name(../../../..) = $sequence[1])])[1] else if ($iAncestors = 5) then ($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[5]) and (name(../..) = $sequence[4]) and (name(../../..) = $sequence[3]) and (name(../../../..) = $sequence[2]) and (name(../../../../..) = $sequence[1])])[1] else 'failure: too much elements for evaluating dyn. xpath ... add another level!'" /> </xsl:otherwise> </xsl:choose> </xsl:function>
For my purpose, only the first matching element that has no child nodes is returned. You probably need to customize this for your specific needs.
Uwe lagler
source share