2009-08-05 7 views
14

No sé si es posible, pero me pregunto cómo hacerlo ...Cómo llamar a plantillas con nombre en función de una variable?

Supongamos que tenemos el siguiente 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> 

¿Es posible cambiar el XSL para leer algo como ...

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

Respuesta

6

no, esto es no es posible no directamente posible. La convención de llamada es:

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

Cuando un QName is defined as:

QName ::= PrefixedName | UnprefixedName 

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

Prefix   ::= NCName 
LocalPart  ::= NCName 

Básicamente esto se reduce a "personajes únicos, no hay expresiones". Como las otras respuestas destacan, son, de hecho, formas de hacer algo equivalente, pero el enfoque directo/enfoque ingenuo no funcionará.

+0

Ah, pero hay un hack feo para evitar esto - ver mi respuesta. –

+0

@Tomalak Esto ha sido posible (aunque en una forma sintáctica diferente) durante casi 8 años :) Consulte mi respuesta para más detalles. –

11

No es posible exactamente como lo describe, pero si desea poder elegir una plantilla en tiempo de ejecución en función de algún valor que establezca en otro lugar, hay un truco para hacerlo. La idea es hacer que su plantilla con nombre también coincida con un nodo con un nombre correspondiente en un modo distinto (para que no estropee su transformación normal), y luego coincida con eso. Por ejemplo:

<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> 

Tenga en cuenta que se puede utilizar en <xsl:with-param><xsl:apply-templates>, por lo que puede hacer todo con esto que usted podría hacer con una llanura <xsl:call-template>.

Además, el código anterior es algo más largo de lo que podría necesitar, ya que trata de evitar el uso de extensiones XSLT. Si su procesador es compatible con exslt:node-set(), puede generar nodos directamente usando <xsl:element>, y usar node-set() para convertir el fragmento de árbol resultante en un nodo plano para que coincida, sin la necesidad de hackear document('').

Para obtener más información, consulte FXSL - se trata de una biblioteca de programación funcional para XSLT basada en este concepto.

2

actualización: Los siguientes enlaces se han actualizado para apuntar a web.archive.org - por desgracia, IDEAlliance ha hecho todos los lenguajes de marcado Exteme actas de la conferencia disponible ... A su debido tiempo, voy a encontrar una mayor lugar permanente para estos dos artículos.


Esto se implementa en FXSL.

Hay buenas explicaciones de los principios fundamentales de FXSL.

Véanse los dos siguientes artículos:

"La programación funcional en XSLT utilizando la biblioteca FXSL" (para XSLT 1.0), (PDF) en:

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

(HTML) en:

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



"Programación Funcional de orden superior con XSLT 2.0 y FXSL "(PDF) en:

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

(HTML) en: http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html



Usando FXSL he sido capaz de resolver facilidad y elegancia muchos problemas, que parecen "imposible XSLT". Uno puede encontrar muchos ejemplos here.

+0

Bienvenido de nuevo. :) – Tomalak

+0

Lamentablemente, ninguno de sus enlaces funciona, ¿puede volver a verificarlos? Tal vez están obligados a una sesión válida o algo así, sigo recibiendo errores de "Página no encontrada". – Tomalak

+0

@Tomalak: Gracias por avisarme. Yo investigaré. Hay algo extraño esta mañana: Facebook es inaccesible y Twitter parece pirateado ... –

2

Creo que tenía más o menos el mismo problema que tú. Tenía una plantilla "externa" y quería llamar a una plantilla "interna" diferente dependiendo de alguna variable configurada en tiempo de ejecución. Encontré tu pregunta buscando en Google una forma de tener un <xsl:call-template> dinámico. Lo resolví utilizando <xsl:apply-templates> en su lugar, de la siguiente manera.

El XML de entrada (generado en tiempo de ejecución) contiene algo a lo largo de las líneas de:

<template name="template_name_1"/> 

El XSL en la plantilla "exterior" tiene:

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

(El select="template" en this apply-templates hace referencia a la etiqueta <template> en el XML de entrada)

Y finalmente, el "interior" plantilla, que quería incluir como resultado del valor del atributo name en mi XML, se ve así:

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

(Una vez más, la match="template[@name='xyz']" se refiere a la anterior select="template" ya su vez a la etiqueta <template> y su atributo name en el XML de entrada)

De esta manera puedo tener una plantilla dinámicamente "llamada" simplemente controlada desde mi XML de entrada.

esto podría no ser el mismo problema que se está tratando de resolver, pero creo que es bastante estrecha, y mucho más simple que las soluciones FSXL mencionados en otra parte de esta página.

3

Para futuras consultas de nadie:

Aquí es un ejemplo de trabajo basado en la respuesta de Pavel Minaev. Sin pensamientos originales de mi parte. ;-) Lo cambié para usar msxml: node-set como él describió (más o menos) para que funcione en .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

Qué tal este?:

<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> 

Se puede simplificar una "llamada" de la plantilla mediante una variable similar a la descrita en una intervención anterior:

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

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

Tenga en cuenta que opcional <xsl:with-param> puede haber usado como de costumbre

Cuestiones relacionadas