Как вызвать именованные шаблоны на основе переменной? - PullRequest
14 голосов
/ 05 августа 2009

Не знаю, возможно ли это, но мне интересно, как это сделать ...

Допустим, у нас есть следующий XSL:

<xsl:template name="foo">
  Bla bla bla
</xsl:template>
...
<xsl:template name="bar">
  Bla bla bla
</xsl:template>
...
<xsl:template match="/">
  <xsl:if test="$templateName='foo'">
    <xsl:call-template name="foo"/>
  </xsl:if>
  <xsl:if test="$templateName='bar'">
    <xsl:call-template name="bar"/>
  </xsl:if>
</xsl:template>

Можно ли изменить XSL, чтобы читать что-то вроде ...

<xsl:template match="/">
  <xsl:call-template name="$templateName"/>
</xsl:template>

Ответы [ 6 ]

12 голосов
/ 06 августа 2009

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

<xsl:stylesheet ... xmlns:t="urn:templates">

  <!-- Any compliant XSLT processor must allow and ignore any elements 
       not from XSLT namespace that are immediate children of root element -->
  <t:templates>
    <t:foo/>
    <t:bar/>
  </t:templates>

  <!-- document('') is the executing XSLT stylesheet -->     
  <xsl:variable name="templates" select="document('')//t:templates" />

  <xsl:template name="foo" match="t:foo" mode="call-template">
    Bla bla bla
  </xsl:template>

  <xsl:template name="bar" match="t:foo" mode="call-template">
    Bla bla bla
  </xsl:template>

  <xsl:template match="/">
    <xsl:variable name="template-name" select="..." />
    <xsl:apply-templates select="$templates/t:*[local-name() = $template-name]"
                         mode="call-template"/>
  </xsl:template>

Обратите внимание, что вы можете использовать <xsl:with-param> в <xsl:apply-templates>, поэтому вы можете делать с этим все, что вы можете делать с простым <xsl:call-template>.

Кроме того, приведенный выше код несколько длиннее, чем вам может понадобиться, поскольку он пытается избежать использования каких-либо расширений XSLT. Если ваш процессор поддерживает exslt:node-set(), то вы можете просто сгенерировать узлы напрямую, используя <xsl:element>, и использовать node-set() для преобразования результирующего фрагмента дерева в простой узел для сопоставления, без необходимости взлома document('').

Для получения дополнительной информации см. FXSL - это библиотека функционального программирования для XSLT, основанная на этой концепции.

6 голосов
/ 05 августа 2009

Нет, это невозможно напрямую невозможно. Соглашение о вызовах:

<xsl:call-template name="QName" />

Где QName определяется как :

QName ::= PrefixedName | UnprefixedName

PrefixedName   ::= Prefix ':' LocalPart
UnprefixedName ::= LocalPart

Prefix         ::= NCName
LocalPart      ::= NCName

В основном это сводится к "только символы, без выражений". Как показывают другие ответы, на самом деле являются способами сделать что-то эквивалентное, но простой подход / наивный подход не будет работать.

3 голосов
/ 18 июля 2013

Для дальнейшего использования:

Вот рабочий пример, основанный на ответе Павла Минаева.Никаких оригинальных мыслей с моей стороны.;-) Я переключил его на использование msxml: node-set, как он описал (более или менее), чтобы он работал в .NET.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
    <xsl:variable name="templates">
        <templates>
            <foo />
            <bar />
        </templates>
    </xsl:variable>
    <xsl:template name="foo" match="foo" mode="call-template">
        <FooElement />
    </xsl:template>
    <xsl:template name="bar" match="bar" mode="call-template">
        <BarElement />
    </xsl:template>
    <xsl:template match="/">
        <Root>
            <xsl:variable name="template-name">bar</xsl:variable> <!-- Change this to foo to get the other template. -->
            <xsl:apply-templates select="msxsl:node-set($templates)/*/*[local-name() = $template-name]" mode="call-template" />
        </Root>
    </xsl:template>
</xsl:stylesheet>
2 голосов
/ 29 марта 2013

Я думаю, у меня была более или менее та же проблема, что и у вас. У меня был «внешний» шаблон, и я хотел вызвать другой «внутренний» шаблон в зависимости от некоторой переменной, установленной во время выполнения. Я нашел ваш вопрос от Google, чтобы получить динамический <xsl:call-template>. Я решил это с помощью <xsl:apply-templates> вместо этого следующим образом.

Входной XML (генерируемый во время выполнения) содержит что-то вроде:

<template name="template_name_1"/>

XSL во "внешнем" шаблоне имеет:

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

(select="template" в этом шаблоне применения относится к тегу <template> во входном XML)

И, наконец, «внутренний» шаблон, который я хотел включить в результате значения атрибута name в моем XML, выглядит следующим образом:

<xsl:template match="template[@name='template_name_1']">
    <!-- XSL / XHTML goes here -->
</xsl:template>

(Опять же, match="template[@name='xyz']" относится к предыдущему select="template" и, в свою очередь, к тегу <template> и его атрибуту name во входном XML)

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

Возможно, это не та проблема, которую вы пытаетесь решить, но я думаю, что она довольно близка и намного проще, чем решения FSXL, упомянутые в другом месте на этой странице.

2 голосов
/ 06 августа 2009

Обновление : ссылки ниже были обновлены, чтобы указывать на web.archive.org - к сожалению, IDEALLIANCE сделал все языки разметки Exteme материалы конференции недоступными ... В свое время я найду более постоянное место для этих двух статей.


Это реализовано в FXSL .

Есть хорошие объяснения основных принципов FXSL.

См. Следующие две статьи:

" Функциональное программирование в XSLT с использованием библиотеки FXSL " (для XSLT 1.0), (PDF) по адресу:

http://web.archive.org/web/20070710091236/http://www.idealliance.org/papers/extreme/proceedings/xslfo-pdf/2003/Novatchev01/EML2003Novatchev01.pdf

(HTML) по адресу:

http://conferences.idealliance.org/extreme/html/2003/Novatchev01/EML2003Novatchev01.html



" Функциональное программирование высшего порядка с XSLT 2.0 и FXSL " (PDF) по адресу:

http://web.archive.org/web/20070222111927/http://www.idealliance.org/papers/extreme/proceedings/xslfo-pdf/2006/Novatchev01/EML2006Novatchev01.pdf

(HTML) по адресу: http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html



Используя FXSL, я смог легко и элегантно решить многие проблемы, которые кажутся «невозможными для XSLT». Здесь можно найти множество примеров здесь .

0 голосов
/ 11 февраля 2015

А как насчет этого?:

  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="xsl:template[@name='foo']" name="foo">
    Bla bla bla foo
      </xsl:template>

      <xsl:template match="xsl:template[@name='bar']" name="bar">
    Bla bla bla bar
      </xsl:template>

      <xsl:template match="/">
        <xsl:variable name="templateName" select="'bar'"/>
        <xsl:apply-templates select="document('')/*/xsl:template[@name=$templateName]"/>
        <xsl:apply-templates select="document('')/*/xsl:template[@name='foo']"/>
      </xsl:template>

    </xsl:stylesheet>

Вы можете упростить «вызов» шаблона, используя переменную, аналогично описанной в предыдущем вкладе:

<xsl:variable name="templates" select="document('')/*/xsl:template"/> 

<xsl:apply-templates select="$templates[@name=$templateName]"/>
<xsl:apply-templates select="$templates[@name='foo']"/>

Обратите внимание, что дополнительный <xsl:with-param> может использоваться как обычно.

...