Подтвердить что ты не робот

В XSLT, как я увеличиваю глобальную переменную из другой области?

Я обрабатываю XML файл, где я хочу сохранить количество узлов, поэтому я могу использовать его как идентификатор, когда пишу новые узлы.

В настоящий момент у меня есть глобальная переменная, называемая "counter". Я могу получить к нему доступ в шаблоне, но я не нашел способ манипулировать им в шаблоне.

Вот сжатая версия моего файла XSLT:

<xsl:variable name="counter" select="1" as="xs:integer"/>

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"></xsl:call-template>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">

   <!-- Increment 'counter' here -->

   <span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>

Любые предложения, как идти отсюда?

4b9b3361

Ответ 1

Другие уже объяснили, как переменные являются неизменными - что в XSLT нет операторов присваивания (как и в случае с чисто функциональными языками программирования).

У меня есть альтернатива решениям, которые были предложены до сих пор. Он избегает передачи параметров (что является многословным и уродливым в XSLT - даже я это признаю).

В XPath вы можете просто подсчитать количество элементов <section>, которые предшествуют текущему:

<xsl:template name="section">
  <span class="title" id="title-{1 + count(preceding-sibling::section)}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

(Примечание: форматирование кода в виде пробелов не будет отображаться в вашем результате, так как текстовые узлы только для пробелов автоматически удаляются из таблицы стилей. Поэтому не нужно вводить инструкции в одну строку.)

Одним из больших преимуществ этого подхода (в отличие от использования position()) является то, что он зависит только от текущего node, а не от текущего списка node. Если вы каким-то образом изменили вашу обработку (например, <xsl:for-each> обрабатывали не только разделы, но и некоторые другие элементы), то значение position() больше не обязательно соответствовало бы позиции элементов <section> в вашем документе. С другой стороны, если вы используете count(), как указано выше, он всегда будет соответствовать положению каждого элемента <section>. Этот подход уменьшает сцепление с другими частями вашего кода, что, как правило, очень хорошо.

Альтернативой count() будет использование команды <xsl:number>. Это поведение по умолчанию будет содержать все одинаковые элементы на одном уровне, который, как вам кажется, является тем, что вы хотите:

<xsl:template name="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

Это компромисс в многословии (требующий дополнительного объявления переменной, если вы все еще хотите использовать фигурные скобки для шаблона атрибута), но только слегка, так как он также значительно упрощает ваше выражение XPath.

Там еще больше возможностей для улучшения. Хотя мы удалили зависимость от текущего списка node, мы все еще зависим от текущего node. Это само по себе не плохо, но не сразу видно, глядя на шаблон, что такое текущий node. Все, что мы знаем, это то, что шаблон называется "section"; чтобы точно знать, что обрабатывается, мы должны искать в другом месте нашего кода. Но даже этого не должно быть.

Если вы когда-либо испытывали желание использовать <xsl:for-each> и <xsl:call-template> вместе (как в вашем примере), откиньтесь назад и выясните, как использовать <xsl:apply-templates>.

<xsl:template match="/doc">
  <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

Этот подход не только менее подробный (<xsl:apply-templates/> заменяет как <xsl:for-each>, так и <xsl:call-template/>), но также сразу становится ясно, что такое текущий node. Все, что вам нужно сделать, это посмотреть на атрибут match, и вы сразу же узнаете, что вы обрабатываете элемент <section>, а элементы <section> - это то, что вы считаете.

Для краткого объяснения того, как работают правила шаблона (т.е. элементы <xsl:template>, имеющие атрибут match), см. "Как работает XSLT" .

Ответ 2

Невозможно изменить переменные XSLT. Вы передадите значение от шаблона к шаблону.

Если вы используете XSLT 2.0, вы можете иметь параметры и использовать туннелирование для распространения переменной в правильные шаблоны.

Ваш шаблон будет выглядеть примерно так:

<xsl:template match="a">
<xsl:param name="count" select="0">
  <xsl:apply-templates>
     <xsl:with-param select="$count+1"/>
  </xsl:apply-templates>
</xsl:template>

Также посмотрите на использование generate-id(), если вы хотите создать идентификаторы.

Ответ 3

Переменные в XSLT неизменяемы, поэтому вам нужно решить эту проблему с учетом этого. Вы можете напрямую использовать position():

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"/>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>

Или более шаблонным способом:

<xsl:template match="/"> 
   <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>

Ответ 4

переменные локально ограничены и читаются только в xslt.

Ответ 5

В зависимости от вашего XSLT-процессора вы можете вводить скриптовые функции в свой XLST. Например, библиотека Microsoft XML поддерживает включение javascript. См. http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspx для примера. Эта тактика, очевидно, не будет работать, если вы планируете развертывать/выполнять XSLT в общедоступных клиентских браузерах; это должно выполняться конкретным XSLT-процессором.

Ответ 6

Вы можете использовать функцию position(), чтобы делать то, что хотите. Это будет выглядеть примерно так.

<xsl:template match="/">
  <xsl:for-each select="section">
    <xsl:call-template name="section">
      <xsl:with-param name="counter" select="{position()}"/>
    </xsl:call-template>
  </xsl:for-each>
</xsl:template>

<xsl:template name="section">
  <xsl:param name="counter"/>
  <span class="title" id="title-{$counter}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

Ответ 7

Не пробовал это сам, но вы можете попробовать и передать параметр в шаблон. В вашем первом шаблоне вы устанавливаете параметр count() (или current(), возможно?) Внутри оператора for-each, а затем передаете это значение вашему шаблону "section".

Здесь далее передача параметров в шаблоны

Ответ 8

Используйте <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> и $RowNum в качестве возрастающего значения.

Например: <xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

Это создаст классы для ссылки со значениями tile1, tile2, tile3 и т.д.