Как использовать переменную XSL в xsl: apply-templates? - PullRequest
9 голосов
/ 07 октября 2010

У меня достаточно сложный вызов xsl: apply-templates:

<xsl:apply-templates select="columnval[@id 
                                       and not(@id='_Name_') 
                                       and not(@id='Group') 
                                       and not(@id='_Count_')]"/>

Выражение повторно используется в других местах, таких как:

<xsl:apply-templates select="someothernode[@id 
                                           and not(@id='_Name_') 
                                           and not(@id='Group') 
                                           and not(@id='_Count_')]"/>

Я хочу как-то обобщить это, чтобы я мог определить его один раз и использовать его в другом месте. Однако, это не похоже на работу:

<xsl:variable name="x">@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')</xsl:variable>
<xsl:apply-templates select="columnval[$x]"/>
<xsl:apply-templates select="someothernode[$x]"/>

Есть ли лучший / другой способ сделать это? Все, что я хочу - это повторно использовать выражение xpath в нескольких различных вызовах xsl: apply-templates (некоторые из которых выбираются из разных потомков).

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

Спасибо.

Ответы [ 6 ]

5 голосов
/ 07 октября 2010

Вы не можете динамически создавать XPath в XSLT (по крайней мере, не XSLT 1.0). Но вы можете легко выполнить то, что вы пытаетесь сделать, используя шаблоны режимов:

<xsl:apply-templates select="columnval" mode="filter"/>
<xsl:apply-template select="someothernode" mode="filter"/>

...

<!-- this guarantees that elements that don't match the filter don't get output -->
<xsl:template match="*" mode="filter"/>

<xsl:template match="*[@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')]" mode="filter">
   <xsl:apply-templates select="." mode="filtered"/>
</xsl:template>

<xsl:template match="columnval" mode="filtered">
   <!-- this will only be applied to the columnval elements that pass the filter -->
</xsl:template>

<xsl:template match="someothernode" mode="filtered">
   <!-- this will only be applied to the someothernode elements that pass the filter -->
</xsl:template>
2 голосов
/ 07 октября 2010

Рефакторинг @ Роберт Росни и @ Томалак

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-templates select="someothernode" mode="filter"/> 

<xsl:template match="*" mode="filter">
   <xsl:param name="pFilter" select="'_Name_|Group|_Count_'"/> 
   <xsl:apply-templates select="self::*
                                [not(contains( 
                                        concat('|',$pFilter,'|'),  
                                        concat('|',@id,'|')))
                                 and @id]"/> 
</xsl:template> 
1 голос
/ 07 октября 2010

И XSLT 1.0, и XSLT 2.0 не поддерживают динамическую оценку.

Один из способов сделать это - использовать <xsl:function> в XSLT 2.0 или <xsl:call-template> в XSLT 1.0 .

 <xsl:function name="my:test" as="xs:boolean">
  <xsl:param name="pNode" as="element()"/>

  <xsl:variable name="vid" select="$pNode/@id"/>

  <xsl:sequence select=
  "$vid and not($vid=('_Name_','Group','_Count_')"/>
 </xsl:function>

, тогда вы можете использовать эту функцию :

<xsl:apply-templates select="columnval[my:test(.)]"/>

Конечно, вы можете указатьтестируйте в определенных шаблонах соответствия, как это было предложено Робертом Россни , и это может быть лучшим способом.

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

Используя этот подход, вы динамически определяете и передаете my:test() в качестве параметра функцию, выполняющую тест.

1 голос
/ 07 октября 2010

Как насчет:

<xsl:variable name="filter" select="_Name_|Group|_Count_" />

<xsl:apply-templates select="columnval" mode="filtered" />
<xsl:apply-templates select="someothernode" mode="filtered" />

<xsl:template match="someothernode|columnval" mode="filtered">
  <xsl:if test="not(contains(
    concat('|', $filter,'|'), 
    concat('|', @id,'|'), 
  ))">
    <!-- whatever -->
  </xsl:if>
</xsl:template>

Вы можете сделать $filter параметром и передать его, например, извне.

Что вы не можете сделать (как вы заметили)) использовать переменные для хранения выражений XPath.

1 голос
/ 07 октября 2010

С расширением exsl: nodeset вы можете создать именованный шаблон, который принимает набор узлов $ x и возвращает отфильтрованный набор узлов согласно вашему статическому предикату.

Вы также можете определить функцию в XSLT 2.0.

1 голос
/ 07 октября 2010

Я бы взглянул на использование расширения для xslt. Я не думаю, что вы можете сделать это в "стандартном" xslt.

Это расширение может делать то, что вы хотите: http://www.exslt.org/dyn/functions/evaluate/index.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...