2009-08-05 3 views
14

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

Допустим, мы имеем следующий 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

нет, это не представляется возможным непосредственно не представляется возможным. Вызывающий конвенция:

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

Где QName is defined as:

QName ::= PrefixedName | UnprefixedName 

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

Prefix   ::= NCName 
LocalPart  ::= NCName 

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

+0

Ах, но есть уродливый взлом, чтобы обойти это - см. Мой ответ. –

+0

@Tomalak Это возможно (хотя в другой синтаксической форме) почти 8 лет :) См. Мой ответ для деталей. –

11

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

<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, основанная на этой концепции.

2

Update: Приведенные ниже ссылки были обновлены, чтобы указать на 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". Можно найти множество примеров here.

+0

С возвращением. :) – Tomalak

+0

К сожалению, обе ваши ссылки не работают, можете ли вы их повторно проверить? Возможно, они связаны с действительной сессией или чем-то еще, я продолжаю получать ошибки «Страница не найдена». – Tomalak

+0

@ Томалак: Спасибо, что сообщили мне. Я буду исследовать. Сегодня утром что-то странное: facebook недоступен, и твиттер кажется взломанным ... –

2

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

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

<template name="template_name_1"/> 

XSL в "космическом" шаблон:

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

(The select="template" в этот шаблон apply ссылается на тег <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, упомянутые в другом месте на этой странице.

3

Для чьего в будущем:

Вот рабочий пример, основанный на ответ Павла Минаева. Никакой оригинальной мысли с моей стороны. ;-) Я переключил его на использование 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> 
0

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

<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> может быть используется как обычно.

Смежные вопросы