XSLT (Transformaciones XSL) es un lenguaje de programación declarativo que permite generar documentos a partir de documentos XML, como ilustra la imagen siguiente:
XSLT se utiliza para obtener a partir de un documento XML otros documentos (XML o no). A un documento XML se le pueden aplicar distintas hojas de estilo XSLT para obtener distintos resultados y una misma hoja de estilo XSLT se puede aplicar a distintos documentos XML.
El lenguaje XSLT está normalizado por el W3C, que ha publicado tres versiones de este lenguaje:
Aunque hay incompatibilidades entre estas versiones, lo que se cuenta en esta lección es válido para todas ellas.
XSLT es un lenguaje declarativo. Por ello, las hojas de estilo XSLT no se escriben como una secuencia de instrucciones, sino como una colección de plantillas (template rules). Cada plantilla establece cómo se transforma un determinado elemento (definido mediante expresiones XPath). La transformación del documento se realiza de la siguiente manera:
Una hoja de estilo XSLT es un documento XML que contiene al menos las etiquetas siguientes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
</xsl:stylesheet>
Estas etiquetas son:
Dentro de la instrucción <xsl:stylesheet> se pueden encontrar los llamados elementos de alto nivel y las plantillas, como en el ejemplo siguiente:
<?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>
Estas etiquetas son
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:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="elemento">
</xsl:template>
</xsl:stylesheet>
Se puede asociar de forma permanente una hoja de estilo XSLT a un documento XML mediante la instrucción de procesamiento <?xml-stylesheet ?>, la misma que permite asociar hojas de estilo CSS. La instrucción de procesamiento <?xml-stylesheet ... ?> va al principio del documento, después de la declaración XML.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="ejemplo.xsl"?>
Cuando se visualiza en un navegador web un documento XML enlazado con una hoja de estilo XSLT, los navegadores muestran el resultado de la transformación, aunque si se muestra el código fuente de la página, los navegadores muestran el documento XML original.
Nota: Google Chrome no muestra los documentos XML que enlazan a hojas de estilo XSLT abiertos como archivos locales (file://...), como se comenta en la lección de diferencias entre navegadores. Firefox sí lo hace.
Vamos a ver ejemplos de plantillas trabajando sobre el documento siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<biblioteca>
<libro>
<titulo>La vida está en otra parte</titulo>
<autor>Milan Kundera</autor>
<fechaPublicacion año="1973"/>
</libro>
<libro>
<titulo>Pantaleón y las visitadoras</titulo>
<autor fechaNacimiento="28/03/1936">Mario Vargas Llosa</autor>
<fechaPublicacion año="1973"/>
</libro>
<libro>
<titulo>Conversación en la catedral</titulo>
<autor fechaNacimiento="28/03/1936">Mario Vargas Llosa</autor>
<fechaPublicacion año="1969"/>
</libro>
</biblioteca>
Si los ejemplos de esta página se abren en el navegador, el resultado no coincide en casi todos los casos con el que se muestra en esta página ya que los navegadores no respetan los saltos de línea ni los espacios en blanco, ni muestran las etiquetas. Los resultados que se muestran en esta página son los que se obtienen con XML Copy Editor.
En el ejemplo siguiente, el resultado incluye el contenido de los nodos <título> y <autor> puesto que no hay ninguna plantilla.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
La vida está en otra parte
Milan Kundera
Pantaleón y las visitadoras
Mario Vargas Llosa
Conversación en la catedral
Mario Vargas Llosa
En el ejemplo siguiente, el resultado incluye el contenido de los nodos <titulo>, ya que no hay regla para ellos, pero los de <autor> se pierden porque la plantilla es vacía.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="autor">
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
La vida está en otra parte
Pantaleón y las visitadoras
Conversación en la catedral
La instrucción <xsl:value-of> extrae el contenido del nodo seleccionado.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<xsl:value-of select="autor"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
Milan Kundera
Mario Vargas Llosa
Mario Vargas Llosa
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<xsl:value-of select="titulo"/>
<xsl:value-of select="autor"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
La vida está en otra parteMilan Kundera
Pantaleón y las visitadorasMario Vargas Llosa
Conversación en la catedralMario Vargas Llosa
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="autor">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
La vida está en otra parte
Milan Kundera
Pantaleón y las visitadoras
Mario Vargas Llosa
Conversación en la catedral
Mario Vargas Llosa
También se pueden extraer los valores de los atributos, utilizando @.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="fechaPublicacion">
<xsl:value-of select="@año"/>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
La vida está en otra parte
Milan Kundera
1973
Pantaleón y las visitadoras
Mario Vargas Llosa
1973
Conversación en la catedral
Mario Vargas Llosa
1969
Se puede generar texto escribiéndolo en la regla, por ejemplo, código html.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>Milan Kundera</p>
<p>Mario Vargas Llosa</p>
<p>Mario Vargas Llosa</p>
Dentro de la regla podemos hacer referencia a varios subnodos.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
<p><xsl:value-of select="titulo"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>Milan Kundera</p>
<p>La vida está en otra parte</p>
<p>Mario Vargas Llosa</p>
<p>Pantaleón y las visitadoras</p>
<p>Mario Vargas Llosa</p>
<p>Conversación en la catedral</p>
Los siguientes ejemplos intentan conseguir el mismo resultado que el ejemplo anterior (ejemplo 3-2), pero utilizando dos reglas, y no lo consiguen:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
<xsl:template match="libro">
<p><xsl:value-of select="titulo"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>La vida está en otra parte</p>
<p>Pantaleón y las visitadoras</p>
<p>Conversación en la catedral</p>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<p><xsl:value-of select="titulo"/></p>
</xsl:template>
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>Milan Kundera</p>
<p>Mario Vargas Llosa</p>
<p>Mario Vargas Llosa</p>
¿Por qué en un caso se obtienen sólo los títulos y en el otro sólo los autores? Porque el procesador XSLT sólo aplica una regla a cada nodo. Si tenemos dos reglas para el mismo nodo, el procesador sólo aplica una de ellas (la última, en este caso).
Además de generar etiquetas, se puede generar texto.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="libro">
<p><xsl:value-of select="autor"/> escribió "<xsl:value-of select="titulo"/>"</p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>Milan Kundera escribió "La vida está en otra parte"</p>
<p>Mario Vargas Llosa escribió "Pantaleón y las visitadoras"</p>
<p>Mario Vargas Llosa escribió "Conversación en la catedral"</p>
La instrucción <xsl:apply-templates> hace que se apliquen a los subelementos las reglas que les sean aplicables.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<h1>Autores</h1>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<html><h1>Autores</h1>
<p>Milan Kundera</p>
<p>Mario Vargas Llosa</p>
<p>Mario Vargas Llosa</p>
</html>
La primera regla sustituye el elemento raíz (y todos sus subelementos) por las etiquetas <html> y <h1>, pero además aplica a los subelementos las reglas que les son aplicables. En este caso, sólo hay una regla para los elementos <libro> que generan los párrafos.
Al transformar un documento, los procesadores XSLT incorporan saltos de línea y espacios en blanco en el resultado, pero no lo hacen de forma uniforme. Por ejemplo, XML Copy Editor y Notepad++ (con el plug-in XML Tools) producen diferentes resultados.
No parece haber una solución sencilla que funcione en todos los procesadores, pero sí soluciones que funcionen en cada uno de ellos.
En el caso de XML Copy Editor, la forma más sencilla de mejorar el formato de presentación de los resultados, eliminando líneas en blanco innecesarias y sangrando los elementos anidados, es utilizar la instrucción <xsl:strip-space>. Pero debe tenerse en cuenta que esta instrucción no produce el mismo resultado en otros procesadores XSLT (como en Notepad++ con XML Tools).
La instrucción <xsl:strip-space> permite indicar si los elementos que contienen únicamente espacios en blanco se incluyen en la transformación o no.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<h1>Autores</h1>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<html>
<h1>Autores</h1>
<p>Milan Kundera</p>
<p>Mario Vargas Llosa</p>
<p>Mario Vargas Llosa</p>
</html>
En el caso de Notepad++ con XML Tools, se puede mejorar el formato de presentación de los resultados, insertando líneas en blanco innecesarias y sangrando los elementos anidados, utilizando la instrucción <xsl:text>. Pero debe tenerse en cuenta que esta instrucción no permite eliminar líneas en blanco que se producen en otros procesadores (como en XML Copy Editor).
La instrucción <xsl:text> permite generar texto que no se puede generar simplemente añadiéndolo (saltos de líneas y espacios en blanco, por ejemplo).
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<xsl:text> </xsl:text>
<h1>Autores</h1>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="libro">
<p><xsl:value-of select="autor"/></p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<html>
<h1>Autores</h1>
<p>Milan Kundera</p>
<p>Mario Vargas Llosa</p>
<p>Mario Vargas Llosa</p>
</html>
La instrucción <xsl:attribute> permite generar un atributo y su valor. Se utiliza cuando el valor del atributo se obtiene a su vez de algún nodo.
Por ejemplo, a partir del siguiente documento XML, se quiere generar la etiqueta <img>. en la que el valor del atributo src sea el contenido de la etiqueta <imagen>.
<?xml version="1.0" encoding="UTF-8"?>
<licencias>
<licencia>
<nombre>Creative Commons By - Share Alike</nombre>
<imagen>cc-bysa-88x31.png</imagen>
</licencia>
</licencias>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="licencia">
<p><img src="<xsl:value-of select="imagen" />" /></p>
</xsl:template>
</xsl:stylesheet>
Error at line 5, column 19: not well-formed (invalid token)
En este caso el problema no es debido a la utilización de comillas dobles (también daría error si se hubieran utilizado comillas simples o entidades), sino que es necesario utilizar la instrucción <xsl:attribute>.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="licencia">
<p><img>
<xsl:attribute name="src">
<xsl:value-of select="imagen" />
</xsl:attribute>
</img>
</p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<p>
<img src="cc-bysa-88x31.png"/>
</p>
En la hoja de estilo XSLT, la etiqueta <img> se escribe con apertura y cierre, aunque en el resultado aparece como etiqueta monoatómica.
De todas formas, el navegador no mostraría todavía la imagen, puesto que no interpreta la etiqueta <img> como la etiqueta de imagen del html.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<xsl:apply-templates />
</html>
</xsl:template>
<xsl:template match="licencia">
<p><img>
<xsl:attribute name="src">
<xsl:value-of select="imagen" />
</xsl:attribute>
</img>
</p>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<html>
<p><img src="cc-bysa-88x31.png"/></p>
</html>