Найти позицию элемента внутри своего родителя с помощью XSLT/XPath

Помимо переписывания большого количества кода XSLT (чего я не собираюсь делать), существует ли способ найти позицию элемента внутри его родителя, когда контекст произвольно установлен на что-то еще? Вот пример:

<!-- Here are my records-->
<xsl:for-each select="/path/to/record">
  <xsl:variable name="record" select="."/>

  <!-- At this point, I could use position() -->
  <!-- Set the context to the current record -->
  <xsl:for-each select="$record">

    <!-- At this point, position() is meaningless because it always 1 -->
    <xsl:call-template name="SomeTemplate"/>
  </xsl:for-each>
</xsl:for-each>


<!-- This template expects the current context being set to a record -->
<xsl:template name="SomeTemplate">

  <!-- it does stuff with the record fields -->
  <xsl:value-of select="SomeRecordField"/>

  <!-- How to access the record position in /path/to or in any other path? -->
</xsl:template>

ПРИМЕЧАНИЕ.. Это упрощенный пример. У меня есть несколько ограничений, препятствующих реализации очевидных решений, таких как передача новых параметров в SomeTemplate и т.д. Я действительно могу только изменить внутренние элементы SomeTemplate.

ПРИМЕЧАНИЕ. Я использую Xalan 2.7.1 с EXSLT. Итак, эти трюки доступны

Любые идеи?

Ответ 1

Вы можете использовать

<xsl:value-of select="count(preceding-sibling::record)" />

или даже, вообще говоря,

<xsl:value-of select="count(preceding-sibling::*[name() = name(current())])" />

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

<xsl:apply-templates select="here/foo|/somewhere/else/bar" />

Информация о местоположении теряется в таком случае, если вы не сохранили ее в переменной и не передали ее вызываемому шаблону:

<xsl:variable name="pos" select="position()" />
<xsl:for-each select="$record">
  <xsl:call-template name="SomeTemplate">
    <xsl:with-param name="pos" select="$pos" />
  </xsl:call-template>
</xsl:for-each>

но, очевидно, это означало бы некоторую перекодировку кода, которую я понимаю, чего вы хотите избежать.


Финальный намек: position() не указывает вам позицию node внутри своего родителя. Он сообщает вам позицию текущего node относительно списка узлов, которые вы обрабатываете прямо сейчас.

Если вы только обрабатываете (т.е. "применяете шаблоны к" или "перебираете" ) узлы внутри одного родителя, это происходит одинаково. Если вы этого не сделаете, это не так.

Окончательный намек № 2: Этот

<xsl:for-each select="/path/to/record">
  <xsl:variable name="record" select="."/>
  <xsl:for-each select="$record">
    <xsl:call-template name="SomeTemplate"/>
  </xsl:for-each>
</xsl:for-each>

эквивалентно этому:

<xsl:for-each select="/path/to/record">
  <xsl:call-template name="SomeTemplate"/>
</xsl:for-each>

но последний работает без разрушения значения position(). Вызов шаблона не меняет контекста, поэтому . будет ссылаться на правильный node с вызываемым шаблоном.