xsl variable example create xml xslt global-variables

xml - variable - xslt transformation



En XSLT, ¿cómo puedo incrementar una variable global desde un ámbito diferente? (8)

Estoy procesando un archivo XML en el que deseo contar el número de nodos, de modo que pueda usarlo como ID a medida que escribo nuevos nodos.

Por el momento tengo una variable global llamada ''contador''. Puedo acceder a él dentro de una plantilla, pero no he encontrado una forma de manipularlo dentro de una plantilla.

Aquí hay una versión condensada de mi archivo 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>

¿Alguna sugerencia de cómo ir desde aquí?


Dependiendo de su procesador XSLT, puede introducir funciones con guiones en su XLST. Por ejemplo, la biblioteca XML de Microsoft admite la inclusión de javascript. Consulte http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspx para ver un ejemplo. Obviamente, esta táctica no funcionará si planea implementar / ejecutar XSLT en buscadores de clientes públicos; tiene que ser hecho por un procesador XSLT específico.


Las variables XSLT no se pueden cambiar. Tendrás que pasar el valor de la plantilla a la plantilla.

Si está utilizando XSLT 2.0, puede tener parámetros y usar el efecto túnel para propagar la variable a las plantillas correctas.

Su plantilla se verá más o menos así:

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

También consulte el uso de generate-id () si desea crear identificadores.


Las variables en XSLT son inmutables, por lo que debe abordar el problema teniendo esto en cuenta. Puede usar position() directamente:

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

O de una manera más orientada a la plantilla:

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


No lo he intentado yo mismo, pero podrías intentar pasar un parámetro a la plantilla. En su primera plantilla, establece el parámetro en count () (o current () ¿tal vez?) Dentro de la sentencia for-each y luego pasa ese valor a su plantilla de "sección".

Aquí hay más sobre pasar parámetros a las plantillas


Otros ya han explicado cómo las variables son inmutables: que no hay sentencias de asignación en XSLT (como en el caso de los lenguajes de programación puramente funcionales en general).

Tengo una alternativa a las soluciones que se han propuesto hasta ahora. Evita el paso de parámetros (que es detallado y feo en XSLT, incluso lo admitiré).

En XPath, simplemente puede contar la cantidad de elementos de <section> que preceden a la actual:

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

(Nota: el formato del código de espacio en blanco no aparecerá en su resultado, ya que los nodos de texto de solo espacio en blanco se eliminan automáticamente de la hoja de estilo. Por lo tanto, no se sienta obligado a poner instrucciones en la misma línea).

Una gran ventaja de este enfoque (en comparación con el uso de position() ) es que depende únicamente del nodo actual, no de la lista de nodos actual. Si cambió su procesamiento de alguna manera (por ejemplo, <xsl:for-each> procesó no solo secciones sino también algún otro elemento), entonces el valor de la position() ya no correspondería necesariamente a la posición de los elementos <section> en su documento. Por otro lado, si usa count() como arriba, siempre corresponderá a la posición de cada elemento <section> . Este enfoque reduce el acoplamiento con otras partes de su código, que generalmente es algo muy bueno.

Una alternativa para count () sería usar la instrucción <xsl:number> . Su comportamiento predeterminado numerará todos los elementos con el mismo nombre en el mismo nivel, que es lo que desea:

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

Es una compensación en verbosidad (que requiere una declaración de variable adicional si aún desea utilizar la plantilla de valor de atributo llaves), pero solo ligeramente, ya que también simplifica drásticamente su expresión XPath.

Todavía hay más margen de mejora. Si bien eliminamos la dependencia de la lista de nodos actual, aún dependemos del nodo actual. Eso, en sí mismo, no es algo malo, pero no está inmediatamente claro al mirar la plantilla cuál es el nodo actual. Todo lo que sabemos es que la plantilla se llama " section "; para estar seguro de lo que se está procesando, debemos buscar en otro lugar de nuestro código. Pero incluso eso no tiene que ser el caso.

Si alguna vez se siente impulsado a usar <xsl:for-each> y <xsl:call-template> juntos (como en su ejemplo), retroceda y descubra cómo usar <xsl:apply-templates> lugar.

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

Este enfoque no solo es menos detallado ( <xsl:apply-templates/> reemplaza tanto a <xsl:for-each> como a <xsl:call-template/> ), sino que también aclara inmediatamente cuál es el nodo actual. Todo lo que tienes que hacer es mirar el atributo de match e inmediatamente sabes que estás procesando un elemento <section> y que los elementos de <section> son lo que estás contando.

Para una explicación sucinta de cómo funcionan las reglas de plantilla (es decir, <xsl:template> elementos que tienen un atributo de match ), consulte "Cómo funciona XSLT" .


Puede usar la función de posición () para hacer lo que desee. Se vería algo como esto.

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


Use <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> y $ RowNum como valor de incremento.

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

Esto creará clases para el enlace con los valores tile1, tile2, tile3, etc ...


las variables tienen un ámbito local y solo se leen en xslt.