Diferencia entre revisiones de «XSLT»

De MediaWiki
Ir a la navegación Ir a la búsqueda
(Página creada con « = XSLT = frame| right | Fluxo de XSLT XSL (''e'''X'''tensible S'''t'''ylesheet '''L'''anguage'', linguaxe de follas de estilo extensible) é un es…»)
 
Línea 146: Línea 146:
 
En este caso la salida sería: Encontramos un curso. Encontramos un tema. Encontramos un tema. Encontramos un tema.
 
En este caso la salida sería: Encontramos un curso. Encontramos un tema. Encontramos un tema. Encontramos un tema.
  
== Plantillas predefinidas ==
+
= Plantillas predefinidas =
 
Cuando tenemos algún elemento del documento origen que no se procesa por ninguna plantilla (es decir, un elemento que no encaja con la expresión de una plantilla ni es hijo de los elementos que encajan), entonces el procesador XSLT les aplica las plantillas predefinidas.
 
Cuando tenemos algún elemento del documento origen que no se procesa por ninguna plantilla (es decir, un elemento que no encaja con la expresión de una plantilla ni es hijo de los elementos que encajan), entonces el procesador XSLT les aplica las plantillas predefinidas.
 
Por ejemplo, si le aplicásemos al documento anterior la siguiente transformación:  
 
Por ejemplo, si le aplicásemos al documento anterior la siguiente transformación:  

Revisión del 03:38 24 ago 2022

XSLT

Fluxo de XSLT

XSL (eXtensible Stylesheet Language, linguaxe de follas de estilo extensible) é un estándar que engloba un conxunto de tres estándares que teñen o obxectivo común de transformar e aplicar estilos aos documentos XML:

XPath
empregado para construir expresións que busquen e accedan a partes concretas do documento XML.
XSLT (XSL Transformations)
é unha linguaxe que nos permite crear unha serie de reglas e patróns que tranformarán o contido do documento orixinal noutro documento XML (ou con outro formato: XHTML, texto, LaTeX, etc.). Un uso típico é para pasar un documento XML a unha web XHTML.
XSL-FO (XSL Formatting Objects)
linguaxe que nos permite formatear os datos contidos no documento XML. Traballa con áreas, bloques, rexións, anchuras e alturas das páginas, dos márxenes, etc. Moi utilizado para xenerar ficheiros PDF a partir de documentos XML.

XSLT permítenos transformar un documento XML engadindo ou eliminando etiquetas ou atributos no documento final, reorganizando os seus elementos, ordenalos segundo algún criterio, etc. Así poderemos crear distintos documentos a partir dun mesmo XML orixe, ou ter distintas vistas da mesma información (dun mesmo documento con información dos meus clientes poido obtener un listado dos morosos, as etiquetas para as cartas que se lles envía ou información sobre a súa facturación, e todo como resultado de aplicar distintos XSLTs ó mesmo documento).

XSLT é unha linguaxe declarativa: as follas de estilo XSLT non se escriben como unha secuencia de instruccións, se non coma unha colección de plantillas (template rules). Cada plantilla establece cómo se transforma un determinado elemento (definido mediante expresións XPath). O seu funcionamento básico consiste en aplicar transformacións a aquelas partes do documento seleccionadas a través dunha expresión XPath que encaixan con alguhna plantilla:

  • O procesador analiza o documento e constrúe a árbore do documento.
  • O procesador recorre a árbore do documento desde o nodo raíz.
  • En cada nodo recorrido o procesador aplica ou non algunha plantilla:
  • Se a un nodo non se lle pode aplicar ningunha plantilla, aplícaselle un patrón predefinido, que copia o seu contenido no documento final (o texto do nodo, non o dos nodos descendentes). A continuación, o procesador recorre os seus nodos hijos.
  • Se a un nodo se lle pode aplicar unha plantilla, se lle aplica. A plantilla pode generar texto que se inclúe no documento final. En principio, o procesador non recorre os seus nodos fillos, salvo que a plantilla indique ó procesador que sí que deben recorrerse os nodos fillos.
  • Cuando o procesador recorreu a árbore, a transformación rematou

Estrutura dun documento

Una hoja de estilo XSLT es un documento XML que tiene la siguiente estructura:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> </xsl:stylesheet>

En lugar de xsl:stylesheet se puede utilizar la etiqueta xls:transform. Esta instrucción es la etiqueta raíz de la hoja de estilo y sus atributos indican la versión y el espacio de nombres correspondiente (un documento XSLT siempre tiene que utilizar un espacio de nombres).

Para enlazar el documento XML a la hoja de estilo, añadiremos antes del elemento raíz del documento XML la siguiente instrucción de procesamiento:

<?xml-stylesheet type="text/xsl" href="archivo.xsl"?>

Dentro de la instrucción <xsl:stylesheet> se pueden encontrar los llamados elementos de alto nivel y las plantillas:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> </xsl:template> </xsl:stylesheet>

Donde:

  • el elemento de alto nivel <xsl:output> indica el tipo de salida producida.
  • la instrucción <xsl:template> es una plantilla.
  • El atributo match indica los elementos afectados por la plantilla y contiene una expresión XPath.
  • El contenido de la instrucción define la transformación a aplicar (si la instrucción no contiene nada, como en el ejemplo anterior, sustituirá el nodo por nada, es decir, eliminará el nodo, aunque conservará el texto contenido en el elemento).

Cuando se aplica una plantilla a un nodo, en principio no se recorren los nodos descendientes. Para indicar que sí queremos recorrer los nodos descendientes y aplicarles las plantillas que les correspondan, hay que utilizar la instrucción <xsl:apply-templates />, como en el ejemplo siguiente:

Plantillas

Para construir plantillas usamos el elemento xsl:template: <xsl:template match=”expresión XPath”> </xsl:template> Estas plantillas contienen reglas que se aplican a los nodos que encajan con su atributo match. El resultado del procesamiento de las reglas conformará el documento resultante de la transformación. Por ejemplo, al siguiente documento XML:

<?xml version="1.0" encoding="UTF-8"?> <Curso horas=”30”> <Asistente>Simon Templar</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Blanche Devereaux</Asistente> </Curso>

se le pueden aplicar cualquiera de las siguientes transformaciones:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Curso">
 </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Asistente"></xsl:template> </xsl:stylesheet>

Cuando aplicamos la primera plantilla, el único elemento del documento origen que cumple la expresión XPath es el elemento raíz (Curso).

Si aplicamos la segunda plantilla, habría cuatro elementos que cumplirían la expresión, y las reglas que forman la plantilla se procesarían una vez por cada uno de ellos.

En lugar del atributo match, las plantillas pueden tener un atributo name. En este caso, lo que hacemos es definir, no aplicar las reglas que componen la plantilla. Estas reglas se aplicarían cuando se llame a la plantilla empleando la siguiente estructura: <xsl:call-template name=”nombreDeLaPlantilla”/>

Aunque no es muy utilizado, también es posible crear plantillas que tengan ambos atributos: match y name.

Contido

Texto: <xsl:text>

Se utiliza para incluir texto en el documento resultante. También se puede escribir el texto directamente en la plantilla, pero utilizando este elemento se pueden controlar los espacios y saltos de línea generados. Las siguientes plantillas son similares:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Asistente"> <xsl:text>Encontrado un asistente!</xsl:text> </xsl:template> </xsl:stylesheet> <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Asistente"> Encontrado un asistente! </xsl:template> </xsl:stylesheet>

Elementos: <xsl:element>

Crea un nuevo elemento en el documento resultante. El atributo name indica el nombre del nuevo elemento. Este atributo se puede componer como combinación de texto, partes del documento original, variables, valores devueltos por funciones, etc.

Atributos: <xsl:attribute>

Crea un nuevo atributo en el elemento del documento resultante. El valor del atributo name será el nombre del nuevo atributo.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Curso"> <xsl:element name="Curso"> <xsl:attribute name="asistentes"> <xsl:value-of select="count(Asistente)"/> </xsl:attribute> <xsl:element name="Horas"> <xsl:value-of select="@horas"/> </xsl:element> </xsl:element> </xsl:template> </xsl:stylesheet>

Daría como resultado:

<Curso asistentes="4"> <Horas>30</Horas> </Curso>

También es posible definir un conjunto de atributos para luego utilizarlos en uno o varios elementos. Esto se realiza con: <xsl:attribute-set>

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:attribute-set name="datos"> <xsl:attribute name="nombre">
<xsl:value-of select="."/> </xsl:attribute> <xsl:attribute name="horas"> <xsl:value-of select="/Curso/@horas"/> </xsl:attribute> </xsl:attribute-set> <xsl:template match="//Asistente"> <xsl:element name="Asistente" use-attribute-sets="datos"/> </xsl:template> </xsl:stylesheet>

El elemento <xsl:value-of> permite obtener el valor devuelto por funciones o el valor de elementos concretos del documento original.

Comentarios: <xsl:comment>

Crea comentarios en el documento de salida con el formato

Instrucciones de procesamiento: <xsl:processing-instruction>

Inserta una instrucción de procesamiento en el documento resultante. Como en los elementos <xsl:element> y <xsl:attribute>, cada elemento <xsl:processing-instruction> tiene un atributo name obligatorio.

Documentos con varias plantillas

Cuando en un documento encontramos varias plantillas, se debe tener claro cómo se procesa el documento origen y cuál será el resultado. Por ejemplo, si sobre el documento XML:

<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="curso2.xsl"?> <Curso horas="30"> <Asistente>Simon Templar</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Blanche Devereaux</Asistente> <Tema>Xeración de documentos XML</Tema> <Tema>Validación de documentos XML</Tema> <Tema>Transformación de documentos XML</Tema> </Curso>

Aplicamos la siguiente transformación:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Asistente"> Encontramos un asistente. </xsl:template> <xsl:template match="//Tema"> Encontramos un tema. </xsl:template> </xsl:stylesheet>

Como los elementos Asistente y Tema están al mismo nivel (ninguno de ellos es hijo del otro), se procesan ambas plantillas y se obtiene el siguiente documento resultante: Encontramos un asistente. Encontramos un asistente. Encontramos un asistente. Encontramos un asistente. Encontramos un tema. Encontramos un tema. Encontramos un tema. Sin embargo, si procesásemos el mismo documento origen con el siguiente documento XSLT: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Curso"> Encontramos un curso. </xsl:template> <xsl:template match="//Asistente"> Encontramos un asistente. </xsl:template> </xsl:stylesheet>

Se podría esperar que el resultado mostrara “Encontramos un asistente” repetido cuatro veces y, a continuación, “Encontramos un curso” una vez. Sin embargo, la salida que produce es simplemente: “Encontramos un curso”.

La razón es que el procesador encuentra la plantilla con la expresión //Asistente que encaja con los elementos Asistente, y la plantilla con la expresión //Curso, que encaja con el elemento Curso. Como los elementos encontrados por la primera expresión ya están contenidos en el elemento Curso (son sus hijos), esta plantilla no llega a aplicarse. Esto es, los elementos Asistente se procesarán con la plantilla de la expresión //Curso, que es de mayor nivel, y no se muestra nada referente a los asistentes en el documento resultante porque las reglas de la plantilla no lo contemplan.

Para que al aplicar una plantilla sobre un elemento continúe procesando sus hijos, tendríamos que incluir el elemento <xsl:apply-templates> dentro de la plantilla. Este elemento tiene un atributo “select” que permite restringir los hijos sobre los que seguir procesando la plantilla: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/Curso"> Encontramos un curso. <xsl:apply-templates select=”Tema”/> </xsl:template> <xsl:template match="Asistente"> Encontramos un asistente. </xsl:template> <xsl:template match="Tema"> Encontramos un tema. </xsl:template>

En este caso la salida sería: Encontramos un curso. Encontramos un tema. Encontramos un tema. Encontramos un tema.

Plantillas predefinidas

Cuando tenemos algún elemento del documento origen que no se procesa por ninguna plantilla (es decir, un elemento que no encaja con la expresión de una plantilla ni es hijo de los elementos que encajan), entonces el procesador XSLT les aplica las plantillas predefinidas. Por ejemplo, si le aplicásemos al documento anterior la siguiente transformación:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Tema"> Encontramos un tema. </xsl:template>
 </xsl:stylesheet>

Obtendríamos el siguiente resultado: Simon Templar Ralph Hinkley Maddie Hayes Blanche Devereaux Encontramos un tema. Encontramos un tema. Encontramos un tema. Básicamente, el comportamiento de las plantillas predefinidas es procesar estos elementos comentados y sus hijos, mostrando el texto que contienen.

Aspectos avanzados de las plantillas

Variables

Al igual que en los lenguajes de programación, podemos definir variables para después utilizar su contenido en la creación de patrones. Para definir una variable usamos el elemento xls:variable: <xsl:variable name=”nombreVariable”> Su valor puede ser una expresión XPath en el atributo select o bien una plantilla contenida en el propio elemento.

Las variables se pueden definir en el interior de una plantilla o fuera, como hijo directo del elemento <xsl:stylesheet>. Según sea el caso, tendremos una variable local a una plantilla o global a todo el documento XSLT.

Para obtener el valor de una variable se utiliza el elemento <xsl:value-of>. El nombre de la variable estará contenido en el atributo select, precedido por el símbolo $. En dicho atributo, en el lugar del nombre de una variable, también podremos poner una expresión XPath. En el caso de que existan múltiples nodos seleccionados por la expresión, se seleccionará el primero de ellos.

El elemento <xsl:value-of> se sustituirá por el valor de la variable o por el valor obtenido por la expresión indicada:

<?xml version="1.0" encoding="UTF-8"?> <<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//Curso">
 <xsl:variable name="numAsistentes" select="count(Asistente)"/> <xsl:element name="Curso"> <xsl:attribute name="asistentes"> <xsl:value-of select="$numAsistentes"/> </xsl:attribute> <xsl:element name="Horas"> <xsl:value-of select="@horas"/> </xsl:element> </xsl:element> </xsl:template> </xsl:stylesheet>

Estructuras de control

Como parte de las plantillas, podemos usar ciertas estructuras de control en el procesamiento de los elementos del documento original.

  • Bucles <xsl:for-each>: El elemento <xsl:for-each select=”expresión XPath”> selecciona cada uno de los nodos del conjunto de nodos resultado de la expresión indicada.
  • Condiciones simples <xsl:if>: El elemento <xsl:if test=”condición”> nos permite generar un contenido únicamente cuando la condición sea cierta.
  • Condiciones múltiples <xsl:choose>: El elemento <xsl:choose> nos permite incluir múltiples condiciones, cada una especificada por el elemento <xsl:when test=”condición”> o <xsl:otherwise>. Cuando se cumple una condición se produce la salida deseada y se abandona la estructura: <xsl:choose> <xsl:when test="condición"> … </xsl:when> <xsl:when test="condición"> … </xsl:when> <xsl:otherwise> … </xsl:otherwise> </xsl:choose>

Estas estructuras de control nos permitirían realizar conversiones más avanzadas. Pongamos que queremos mostrar el siguiente documento XML con notas de alumnos:

<?xml version="1.0" encoding="UTF-8"?>
 <?xml-stylesheet type="text/xsl" href="Notas por Cores.xslt"?> <alumnos> <alumno> <nombre>Perico dos Palotes</nombre> <nota>8</nota> </alumno> <alumno> <nombre>Arsenio Lupin</nombre> <nota>9.7</nota> </alumno> <alumno> <nombre>Frodo Bolson</nombre> <nota>4.6</nota> </alumno> <alumno> <nombre>Smeagol</nombre> <nota>6.2</nota> </alumno> <alumno> <nombre>Pulgarcito</nombre> <nota>7.6</nota> </alumno> </alumnos>

En forma de tabla HTML con colores alternos para el fondo de las filas y con distinto color en función de la nota:


<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <h1>Notas</h1> <table border="1"> <tr bgcolor="gray"> <th>Alumno</th> <th>Nota</th> </tr> <xsl:for-each select="alumnos/alumno"> <xsl:text disable-output-escaping="yes"> <tr </xsl:text> <xsl:if test="(position() mod 2) = 1"> bgcolor="yellow" </xsl:if> <xsl:text disable-output-escaping="yes"> > </xsl:text>
 <td><xsl:value-of select="nombre"/></td> <xsl:choose> <xsl:when test="nota < 5"> <td bgcolor="red"><xsl:value-of select="nota"/></td> </xsl:when> <xsl:when test="nota < 7"> <td bgcolor="yellow"><xsl:value-of select="nota"/></td> </xsl:when> <xsl:otherwise> <td bgcolor="green"><xsl:value-of select="nota"/></td> </xsl:otherwise> </xsl:choose> <xsl:text disable-output-escaping="yes"> <tr/> </xsl:text> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>

Otra opción sería utilizar plantillas:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/alumnos"> <html> <body> <h1>Notas</h1> <table border="1"> <tr bgcolor="gray"> <th>Alumno</th> <th>Nota</th> </tr> <xsl:apply-templates select="alumno"/> </table> </body> </html> </xsl:template> <xsl:template match="alumno">
 <xsl:text disable-output-escaping="yes"> &lt;tr </xsl:text> <xsl:if test="(position() mod 2) = 1"> bgcolor="yellow" </xsl:if> <xsl:text disable-output-escaping="yes"> &gt; </xsl:text> <td> <xsl:value-of select="nombre"/> </td> <xsl:choose> <xsl:when test="nota < 5"> <td bgcolor="red"><xsl:value-of select="nota"/></td> </xsl:when> <xsl:when test="nota < 7"> <td bgcolor="yellow"><xsl:value-of select="nota"/></td> </xsl:when> <xsl:otherwise> <td bgcolor="green"><xsl:value-of select="nota"/></td> </xsl:otherwise> </xsl:choose> <xsl:text disable-output-escaping="yes"> &lt;/tr&gt; </xsl:text> </xsl:template> </xsl:stylesheet>

Ordenar os resultados

Para ordenar los resultados tendremos que emplear el elemento <xsl:sort select=”elementoXML”/> dentro del <xsl:for-each> o <xsl:apply-templates> que corresponda. Este elemento puede tener 3 atributos:

  • select: indica cual será el elemento por el que se ordenarán los resultados.
  • order: puede ser ascending o descending
  • data-type: puede ser number o text.

En el ejemplo anterior, para ordenar la tabla por la nota del alumno: ... <xsl:for-each select="alumnos/alumno"> <xsl:sort select="nota" data-type="number" order="descending"/> ...

Copiar elementos del documento origen

XSLT permite copiar en el documento resultante un nodo o un conjunto de nodos del documento origen. Esta copia la podemos hacer de dos maneras:

  • <xsl:copy>: realiza una copia sencilla del elemento al que se refiere la plantilla. No copia sus atributos, su texto o sus hijos, solamente el elemento. Si quisiéramos procesar también su contenido, habría que hacerlo de forma específica.
  • <xsl:copy-of>: hace una copia profunda y traslada al documento resultante el texto, los hijos y los atributos del original. A diferencia de <xsl:copy>, necesita del atributo select para seleccionar los nodos que debe copiar.

Por ejemplo, el documento siguiente: <?xml version="1.0" encoding="UTF-8"?> <Curso horas="30"> <Asistente>Simon Templar</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Blanche Devereaux</Asistente> </Curso> Lo podríamos copiar de las siguientes maneras:

XSLT xls:copy

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/Curso"> <Asistentes> <xsl:apply-templates/> </Asistentes> </xsl:template> <xsl:template match="Asistente"> <xsl:copy/> </xsl:template> </xsl:stylesheet>

Resultado:

<Asistentes> <Asistente /> <Asistente /> <Asistente /> <Asistente /> </Asistentes>

xls:copy-of

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:copy-of select="Curso"/> </xsl:template> </xsl:stylesheet>
<Curso> <Asistente>Simon Templar</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Blanche Devereaux</Asistente> </Curso>

También se podrían hacer copias idénticas del documento origen con xls:copy, seleccionando todos los elementos y atributos de la siguiente forma:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>


Aplicar varias plantillas al mismo nodo

Por defecto, un procesador XSLT selecciona la plantilla que se va a aplicar a cada uno de los nodos que forman el documento XML que se procesa. Si lo que queremos es transformar uno o varios nodos de dos o más formas diferentes, necesitamos que el procesador aplique más de una plantilla sobre el mismo nodo.

Para conseguir esto, empleamos el atributo mode. Este atributo permite definir más de una plantilla para el mismo nodo. Se aplica a los elementos <xsl:template> y <xsl:applytemplates>. Si varias plantillas se aplican al mismo nodo, los valores de sus atributos mode deben ser distintos.

Por ejemplo, para ordenar la lista de asistentes de forma ascendente primero, y descendiente después, lo podemos hacer de la siguiente manera:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:element name="Asistentes"> <xsl:element name="Asistentes_Asc"> <xsl:apply-templates mode="asc"/> </xsl:element> <xsl:element name="Asistentes_Desc"> <xsl:apply-templates mode="desc"/> </xsl:element> </xsl:element> </xsl:template> <xsl:template match="Curso" mode="asc"> <xsl:for-each select="Asistente"> <xsl:sort select="."/> <xsl:copy-of select="."/> </xsl:for-each> </xsl:template> <xsl:template match="Curso" mode="desc"> <xsl:for-each select="Asistente"> <xsl:sort select="." order="descending"/> <xsl:copy-of select="."/> </xsl:for-each> </xsl:template> </xsl:stylesheet>

Lo que produciría la siguiente salida:

<Asistentes> <Asistentes_Asc> <Asistente>Blanche Devereaux</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Simon Templar</Asistente> </Asistentes_Asc> <Asistentes_Desc> <Asistente>Simon Templar</Asistente> <Asistente>Ralph Hinkley</Asistente> <Asistente>Maddie Hayes</Asistente> <Asistente>Blanche Devereaux</Asistente> </Asistentes_Desc> </Asistentes>

Claves e identificadores

Al procesar un documento XML podemos crear claves para identificar elementos del documento original. Los procesadores XSLT suelen crear índices internos para las claves, lo que mejora el acceso a su contenido. Para crear una clave se utiliza el elemento <xsl:key>. Este elemento debe ser hijo directo de <xsl:stylesheet>. No se puede utilizar en ningún otro sitio, como por ejemplo formando parte de una plantilla. Tiene tres atributos requeridos:

  • name: asigna un nombre a la clave para poder utilizarla posteriormente
  • match: indica los nodos que serán indexados por la clave
  • use: define la propiedad de los nodos que se empleará para crear el índice. Esta propiedad es la que usaremos para acceder a los nodos del índice.

Por ejemplo, si quisiéramos definir una clave para los alumnos sobre su nombre:

<?xml version="1.0" encoding="UTF-8"?> <alumnos> <alumno> <nombre>Perico dos Palotes</nombre> <nota>8</nota> </alumno> <alumno> <nombre>Arsenio Lupin</nombre> <nota>7</nota> </alumno> <alumno> <nombre>Frodo Bolson</nombre> <nota>7</nota> </alumno> <alumno> <nombre>Smeagol</nombre> <nota>4</nota> </alumno> <alumno> <nombre>Pulgarcito</nombre> <nota>8</nota> </alumno> </alumnos>

Faríamos o seguinte:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="clave_alumno" match="alumno" use="nombre"/> </xls:stylesheet>

Una vez creada la clave, utilizando la función key() obtendremos los nodos de la clave indicando la propiedad definida en el atributo “use”. En nuestro ejemplo, si posteriormente quisiéramos obtener a partir del nombre de un alumno la nota correspondiente, haríamos:

<xsl:template match="/">
<xsl:value-of select="key('clave_alumno', 'Pulgarcito')/nota" /> </xsl:template>

Identificadores de nodos

Existe una función específica de XSLT: generate-id, que recibe como parámetro un nodo, y devuelve un valor único generado aleatoriamente. Este valor será único en todo el documento, y además será siempre el mismo para ese nodo y esa ejecución, es decir, si más adelante empleamos de nuevo la función generate-id con el mismo nodo, obtendremos el mismo valor.

Se usa, por ejemplo, para generar identificadores y enlaces internos a ellos mismos en un documento HTML.

<a name="{generate-id(.)}"> <xsl:value-of select="nombre" /> </a><!-- y generamos el enlace --> <a href="#{generate-id(.)}"> Para ir al elemento, pinche aquí </a>

Agrupamiento de nodos

Gracias a las claves y a la generación de identificadores, podemos realizar una aplicación interesante que es el agrupamiento de nodos. Se hace en dos pasos: un bucle for-each selecciona el primer nodo de cada grupo y otro bucle dentro del primero crea los elementos del grupo. En nuestro caso podemos hacer un grupo por cada valor de la nota, y mostrar dentro de cada uno, a los alumnos que obtuvieron dicha nota.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="clave_alumno" match="alumno" use="nota"/> <xsl:template match="/"> <xsl:element name="Notas"> <xsl:for-each select="/alumnos/alumno[generate-id(.)=generate-id(key('clave_alumno',nota))]"> <xsl:sort select="nota" order="descending" /> <xsl:element name="Nota"> <xsl:attribute name="valor"> <xsl:value-of select="nota" /> </xsl:attribute>
<xsl:for-each select="key('clave_alumno',nota)"> <xsl:element name="Alumno"> <xsl:value-of select="nombre" /> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>

El primer bucle recorre los alumnos y, con la condición indicada en la expresión XPath, se queda solamente con uno por cada valor diferente de la nota. La función key('clave_alumno',nota) busca la nota del nodo que procesa el bucle (nota) en la clave creada sobre la nota, y devuelve el nodo correspondiente al primer alumno de todos los que comparten esa nota. Después empleando generate-id comprobamos si el nodo que está procesando el bucle (.) es el mismo que devuelve la función key. Si no es el caso, no se cumple la condición (no es el primer nodo con esa misma nota) y no se procesan los elementos del bucle. El segundo bucle utiliza la clave para procesar todos los nodos con una misma nota. El resultado será el siguiente:

<?xml version="1.0" encoding="UTF-8"?> <Notas> <Nota valor="8"> <Alumno>Perico dos Palotes</Alumno> <Alumno>Pulgarcito</Alumno> </Nota> <Nota valor="7"> <Alumno>Arsenio Lupin</Alumno> <Alumno>Frodo Bolson</Alumno> </Nota> <Nota valor="4"> <Alumno>Smeagol</Alumno> </Nota> </Notas>

Formato do resultado

Con la etiqueta <xsl:output …> podemos definir distintas características sobre el formato del documento de salida (todas son opcionales):

<xsl:output method="xml|html|text" version="string" encoding="string" omit-xml-declaration="yes|no" standalone="yes|no" doctype-public="string" doctype-system="string" cdata-section-elements="namelist" indent="yes|no" media-type="string"/>

Es importante el atributo method. Si no se incluye, el formato de salida por defecto es XML (a no ser que el elemento raíz del documento sea html: en este caso el formato de salida será HTML si no se indica ningún otro). Aunque depende del procesador XSLT que utilicemos, en la mayoría de las ocasiones si el documento resultante es XML se incluirá en el mismo, de forma automática, una declaración <?xml … ?>.