Да, мы можем ... по крайней мере, элементарно. Вот обходной путь, который я использую с Saxon CE (XSLT 2.0), пока не будет доступна функция «оценки». Возможно, это не работает для всех видов сложных XML-документов, но, возможно, вы можете настроить «фильтр» так, как вам нужно (запрос по атрибутам и т. Д.).
В моей особой ситуации у меня есть выражения xPath, описывающие «полный» путь к элементам, включая их имена, и хитрость заключается в том, чтобы использовать подстановочный знак в сочетании только с последним элементом динамического выражения xPath, например, используйте «третий» вместо «первый / второй / третий»:
<xsl:variable name="value" select="//*[name() = 'third']" />
Для того, чтобы ограничить результат (выделите все элементы с именем «третий»), вам также придется запросить предков «первый» и «второй». Может быть, у кого-нибудь есть идея упростить следующий код, в частности, вызов предков:
<!-- global variable which holds a XML document with root node "data" -->
<xsl:variable name="record" select="document('record.xml')/data"/>
<!-- select elements from the global "record" variable using dynamic xpath expressions -->
<xsl:function name="utils:evaluateXPath">
<xsl:param name="xpath" as="xs:string"/>
<xsl:choose>
<xsl:when test="function-available('evaluate')">
<!-- modify the code if the function has been implemented :-) -->
<xsl:value-of select="'function evaluate() can be used ...'"/>
</xsl:when>
<xsl:otherwise>
<!-- get a list of elements defined in the xpath expression -->
<xsl:variable name="sequence" select="tokenize($xpath, '/')" />
<!-- get the number of ancestors for the last element -->
<xsl:variable name="iAncestors" select="count($sequence)-1" as="xs:integer" />
<!-- get the last element from the xpath expression -->
<xsl:variable name="lastElement" select="if ($iAncestors > 0) then $sequence[last()] else $xpath" />
<!-- try to find the desired element as defined in xpath expression -->
<!-- use parenthesis to grab only the first occurrence -->
<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>
Что касается моей цели, то возвращается только первый соответствующий элемент, который не имеет дочерних узлов. Возможно, вам придется настроить это для ваших конкретных потребностей.