XSLT: контекст XPath и document()

У меня есть XSLT, как это:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                         xmlns:xalan="http://xml.apache.org/xalan">

  <xsl:variable name="fooDocument" select="document('fooDocument.xml')"/>

  <xsl:template match="/">
    <xsl:apply-templates select="$fooDocument//*"/>
  </xsl:template>

  <xsl:template match="nodeInFooDocument">
    <xsl:variable name="valueFromSource" select="//someSourceElement"/>
  </xsl:template>
</xsl:transform>

Во втором шаблоне, который соответствует узлам в fooDocument.xml, который загружается с document(), я хочу получить доступ к узлам в источнике XML, над которым выполняется преобразование. Это не работает с //someSourceElement, потому что, видимо, XPath выполняет этот путь в контексте fooDocument.

Первое обходное решение, которое приходит на ум, это:

...
<!-- global variable -->
<xsl:variable name="root" select="/"/>

...
<!-- in the template -->
<xsl:variable name="valueFromSource" select="$root//someSourceElement"/>

...

Но я не могу использовать этот обходной путь, потому что на самом деле моя переменная выбрана так:

<xsl:variable name="valueFromSource" select="xalan:evaluate($someXPathString)"/>

$someXPathString создается не в XSLT-файле, а загружается из fooDocument (и содержит абсолютный путь, как тот, что использовался выше). Тем не менее, мне нужно каким-то образом изменить контекст XPath обратно на источник XML. Я нашел очень хакерский обходной путь:

<xsl:for-each select="$root[1]">
  <xsl:variable name="valueFromSource" select="xalan:evaluate($someXPathString)"/>
</xsl:for-each>

(Бесполезный) цикл for-each изменяет контекст обратно на основной источник XML, поэтому XPath оценивает правильно. Но очевидно, что это неприемлемое решение.

Есть ли способ сделать это правильно или кто-нибудь может предложить лучший обходной путь?


person flyx    schedule 16.12.2011    source источник


Ответы (2)


Даже если вы считаете, что ваша попытка с помощью for-each select="$root" изменить документ контекста неприемлема, это правильный подход. Так что пользуйтесь этим, другого выхода нет.

person Martin Honnen    schedule 16.12.2011
comment
Я согласен с тем, что for-each — это прекрасный способ изменить контекстный документ (хотя и несколько неинтуитивный), но, конечно, Мартин, вы не имеете в виду, что другого пути нет. Например. apply-templates select="$root" (с режимом или без) также установит контекстный документ, верно? - person LarsH; 17.12.2011
comment
Я не нашел другого возможного способа изменить документ контекста. - person flyx; 12.01.2012

Рассматривали ли вы выполнение всех вычислений для создания $someXPathString с использованием ряда глобальных переменных?

<xsl:variable name="fooDocument" select="document('fooDocument.xml')"/>

<xsl:variable name="temp1"
  .. some computation using fooDocument ..
</xsl:variable>

<xsl:variable name="temp2"
  .. some computation using temp1 ..
</xsl:variable>

<xsl:variable name="someXPathString"
  .. some computation using temp2 ..
</xsl:variable>

<xsl:variable name="root" select="xalan:evaluate($someXPathString)"/>
person Michael Kay    schedule 16.12.2011
comment
Это сработает, если я решу скопировать все содержимое fooDocument в глобальную переменную и использовать для нее xalan:nodeset(). Поскольку fooDocument содержит последовательность произвольной длины, я не могу разбить ее на фиксированное количество глобальных переменных, содержащих примитивные значения. Для удобочитаемости это определенно вариант. РЕДАКТИРОВАТЬ: Если подумать, когда я использую apply-templates для этой глобальной переменной, мой контекст все равно будет не в источнике XML, а в переменной, которая содержит результат вызова xalan:nodeset(). - person flyx; 19.12.2011