XSLT

De MediaWiki
Ir a la navegación Ir a la búsqueda


Xsl icon.png

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.
  • Cando o procesador recorreu a árbore, a transformación rematou

Versións

É un estándar do W3C con tres versións:

Podes ver un resumo das melloras entre versións na Wikipedia, e exemplos das novas posibiidades da versión 3.0.

Estrutura dun documento

Unha folla de estilo XSLT é un documento XML que ten a seguinte estrutura:

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

No lugar de xsl:stylesheet pódese utilizar a etiqueta xls:transform. Esta instrucción é a etiqueta raíz da folla de estilo e os seus atributos indican a versión e o espazo de nomes correspondente (un documento XSLT sempre ten que utilizar un espazo de nomes).

Para enlazar o documento XML á folla de estilo engadiremos antes do elemento raíz do documento XML a seguinte instrucción de procesamento:

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

Dentro da instrucción <xsl:stylesheet> podense atopar os denominados elementos de alto nivel e as 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>

Onde:

  • o elemento de alto nivel <xsl:output> indica o tipo de saída producida.
  • a instrucción <xsl:template> é unha plantilla.
  • O atributo match indica os elementos afectados pola plantilla e contén una expresión XPath.
  • O contido da instrucción define a transformación a aplicar (se a instrucción non contén nada coma no exemplo anterior, sustituirá o nodo por nada, o que quere dicir que eliminará o nodo, aínda que conservará o texto contido no elemento).

Cando se aplica unha plantilla a un nodo, en principio non se recorren os nodos descendentes. Para indicar que sí queremos recorrer os nodos descendentes e aplicarlles as plantillas que lles correspondan hao que utilizar a instrucción <xsl:apply-templates />, coma no seguinte exemplo:

PENDIENTE

Plantillas

Para construir plantillas usamos o elemento xsl:template:

<xsl:template match=”expresión XPath”> </xsl:template>

Estas plantillas conteñen reglas que se aplican aos nodos que encaixan co seu atributo match. O resultado do procesamiento das reglas conformará o documento resultante da transformación.

Por exemplo, ó seguinte documento XML:

<?xml version="1.0" encoding="UTF-8"?>
<Curso horas=”40”>
    <Asistente>Pepe Carballeira</Asistente>
    <Asistente>Rafael Chousa</Asistente>
    <Asistente>Ana Chamadoira</Asistente>
    <Asistente>Alba Pereira</Asistente>
</Curso>

Se lle poden aplicar calquiera das seguintes transformacións:

<?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 a primeira plantilla o único elemento do documento orixe que cumple a expresión XPath é o elemento raíz (Curso).

Aplicando a segunda plantilla, hai catro elementos que cumplirán a expresión, e as reglas que forman a plantilla procesaríanse unha vez por cada un deles.

As plantillas poden ter un atributo name en lugar do atributo match . Neste caso o que facemos é definir, non aplicar as reglas que compoñen a plantilla. Estas reglas aplicaríanse cando se chame á plantilla empregando a seguinte estrutura:

<xsl:call-template name=”nombreDeLaPlantilla”/>

Aínda que non é moi utilizado é posible crear plantillas que teñan ambos atributos: match e name.

Contido

Texto: <xsl:text>

Útilizase para incluir texto no documento resultante. Tamén se pode escribir ó texto directamente na plantilla, pero utilizando este elemento pódense controlar os espazos e saltos de liña xenerados.

As seguintes 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 novo elemento no documento resultante. O atributo name indica o nome do nuevo elemento. Este atributo pódese compoñer coma:

  • combinación de texto
  • partes do documento orixinal
  • variables
  • valores devoltos por funcións
  • etc.

Atributos: <xsl:attribute>

Crea un novo atributo no elemento do documento resultante. O valor do atributo name será o nome do novo 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>

Tamén é posible definir un conxunto de atributos para logo utilizalos en un ou varios elementos. Esto 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>

O elemento <xsl:value-of> permite obter o valor devolto por funcións ou o valor de elementos concretos do documento orixinal.

Comentarios: <xsl:comment>

Crea comentarios no documento de saída co formato

Instruccións de procesamento: <xsl:processing-instruction>

Inserta una instrucción de procesamento no documento resultante. Coma nos elementos <xsl:element> e <xsl:attribute>, cada elemento <xsl:processing-instruction> ten un atributo name obrigatorio.

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="40">
    <Asistente>Pepe Carballeira</Asistente>
    <Asistente>Rafael Chousa</Asistente>
    <Asistente>Ana Chamadoira</Asistente>
    <Asistente>Alba Pereira</Asistente>
    <Tema>Xeración de documentos XML</Tema>
    <Tema>Validación de documentos XML</Tema>
    <Tema>Transformación de documentos XML</Tema>
</Curso>

Aplicamos a seguinte 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>

Coma os elementos Asistente y Tema están ó mesmo nivel (ningún deles é fillo do outro), procésanse ambas plantillas e obtense o siguiente documento resultante:

Encontramos un asistente. 
Encontramos un asistente. 
Encontramos un asistente. 
Encontramos un asistente. 
Encontramos un tema. 
Encontramos un tema. 
Encontramos un tema.

Mais se procesásemos o mesmo documento orixe co seguinte 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>

O resultado amosaría “Encontramos un asistente” repetido catro veces e a continuación “Encontramos un curso” unha vez? Non. A saída que produce é simplemente:

“Encontramos un curso”.

A razón é que o procesador atopa a plantilla coa expresión //Asistente que encaixa cos elementos Asistente, e a plantilla coa expresión //Curso, que encaixa co elemento Curso. Coma os elementos atopados pola primeira expresión xa están contidos no elemento Curso (son os seus fillos), esta plantilla non chega a aplicarse.

Porén, os elementos Asistente procesaranse coa plantilla da expresión //Curso, que é de maior nivel, e non se mostra nada referente aos asistentes no documento resultante porque as reglas da plantilla non o contemplan.

Para que ó aplicar unha plantilla sobre un elemento continúe procesando os seus fillos, tenriamos que incluir o elemento <xsl:apply-templates> dentro da plantilla. Este elemento tene un atributo select que permite restrinxir os fillos sobre os que seguir procesando a 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>
</xsl:stylesheet>

Neste caso a saída sería:

Encontramos un curso. 
Encontramos un tema. 
Encontramos un tema. 
Encontramos un tema.

Plantillas predefinidas

Cando tenemos algún elemento do documento orixe que non se procesa por ningunha plantilla (isto é, un elemento que non encaixa coa expresión dunha plantilla nin é fillo dos elementos que encaixan), entón o procesador XSLT aplicalles as plantillas predefinidas.

Por exemplo, se lle aplicásemos ao documento anterior a seguinte 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>

Obteríamos o seguiente resultado:

Pepe Carballeira
Rafael Chousa
Ana Chamadoira
Alba Pereira
Encontramos un tema. 
Encontramos un tema. 
Encontramos un tema.

Básicamente, o comportamento das plantillas predefinidas é procesar estes elementos comentados e os seus fillos amosando o texto que conteñen.

Aspectos avanzados das plantillas

Estruturas de control

Coma parte das plantillas podemos usar certas estruturas de control no procesamento dos elementos do documento orixinal.

Bucles: <xsl:for-each>

O elemento <xsl:for-each select=”expresión XPath”> selecciona cada un dos nodos do conxunto de nodos resultado da expresión indicada.

Condicións simples: <xsl:if>

O elemento <xsl:if test=”condición”> permite xenerar un contido únicamente cando a condición sexa certa.

Condicións múltiples: <xsl:choose>, <xsl:when> e <xsl:otherwise>

O elemento <xsl:choose> permite incluir múltiples condicións, cada unha especificada polo elemento <xsl:when test=”condición”> ou <xsl:otherwise>.

Cando se cumple unha condición prodúcese a saída desexada e abandónase a estrutura:

<xsl:choose>
    <xsl:when test="condición"></xsl:when>
    <xsl:when test="condición"></xsl:when>
    <xsl:otherwise></xsl:otherwise>
</xsl:choose>

Conversións máis avanzadas coas estruturas de control

Estas estruturas de control permiten realizar conversións más avanzadas. Se queremos amosar o seguinte 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>

E o queremos en forma de táboa HTML con cores alternas para o fondo das filas e con distinta cor en función da 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>

Outra 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: <xsl:sort >

Para ordenar os resultados teremos que empregar o elemento <xsl:sort select=”elementoXML”/> dentro do <xsl:for-each> ou <xsl:apply-templates> que corresponda.

Este elemento pode ter tres atributos:

  • select: indica cal será o elemento polo que se ordearán os resultados.
  • order: pode ser ascending ou descending
  • data-type: pode ser number ou text

No exemplo anterior, para ordear a táboa pola nota do alumno:

... 
<xsl:for-each select="alumnos/alumno"> 
<xsl:sort select="nota" data-type="number" order="descending"/> 
...

Variables: <xsl:variable>

Do mesmo xeito que nas linguaxes de programación podemos definir variables para despois utilizar o seu contido.

Para definir una variable empregamos o elemento:

<xsl:variable name=”nomeVariable”>

O seu valor pode ser unha expresión XPath no atributo select ou ben unha plantilla contida no propio elemento.

As variables podense definir no interior dunha plantilla ou fora, coma fillo directo do elemento <xsl:stylesheet>. Segundo sea o caso, teremos unha variable local a unha plantilla ou global a todo o documento XSLT.

Para obter o valor dunha variable utilízase o elemento <xsl:value-of>. O nome da variable estará contido no atributo select precedido polo símbolo $. Nese atributo, en lugar do nome dunha variable tamén podremos poñer unha expresión XPath. No caso de que existan múltiples nodos seleccionados pola expresión, seleccionarase o primeiro deles.

O elemento <xsl:value-of> substituirase polo valor da variable ou polo valor obtido pola 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>

Parámetros: <xsl:param> e <xsl:with-param>

xsl:with-param permite definir un parámetro que se lle pasará a una plantilla.

Atributos:

  • name:nombe do parámetro
  • select:ExpresiónXPath(opcional)

xsl:param Se é declarado coma elemento de alto nivel será un parámetro global e Se se declara dentro dunha plantilla é local e ten que ir asociado a un elemento xsl:with-param.

Atributos:

  • name:nome do parámetro
  • select:ExpresiónXPath(opcional)

Exemplo:

<xsl:templatematch="/">
 <html>
 <body>
  <xsl:for-eachselect=“//persona">
   <xsl:call-templatename=“mostrar_dni">
   <xsl:with-paramname=“dni" select= “@dni" />
   </xsl:call-template>
  </xsl:for-each>
 </body>
 </html>
</xsl:template>
<xsl:templatename= “mostrar_dni" >
 <xsl:paramname= “dni" />
  <p>DNI: <xsl:value-ofselect= "$dni" /></p>
 </xsl:template>
</xsl:stylesheet>

Copiar elementos do documento orixe: <xsl:copy> e <xsl:copy-of>

XSLT permite copiar no documento resultante un nodo ou un conxunto de nodos do documento orixe. Esta copia pódese facer de dous xeitos:

  • <xsl:copy>: realiza unha copia sinxela do elemento ó que se refire a plantilla. Non copia os seus atributos, o seu texto ou os seus fillos e so ó elemento. Se quixeramos procesar tamén o seu contenido habería que facelo de xeito específico.
  • <xsl:copy-of>: fai unha copia profunda e traslada ó documento resultante o texto, os fillos e os atributos do orixinal. A diferencia de <xsl:copy>, necesita do atributo select para seleccionar os nodos que debe copiar.

Por ejemplo, o documento seguinte:

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

Poderíamolo copiar dos seguintes xeitos:

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>

Tamén se poderían facer copias idénticas do documento orixe con xls:copy, seleccionando todos os elementos e atributos así:

<?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 ao mesmo nodo: <xsl:template> e <xsl:applytemplates> con atributo mode

Por defecto, un procesador XSLT selecciona a plantilla que se vai aplicar a cada un dos nodos que forman o documento XML que se procesa. Se o que desexamos é transformar un ou varios nodos de dúas ou máis formas diferentes, necesitamos que o procesador aplique máis dunha plantilla sobre o mesmo nodo: empregaremos o atributo mode.

Este atributo permite definir máis dunha plantilla para o mesmo nodo. Aplicase aos elementos <xsl:template> e <xsl:applytemplates>. Se varias plantillas se aplican ao mesmo nodo, os valores dos seus atributos mode deben ser distintos.

Por exemplo, para ordenar a lista de asistentes de forma ascendente primeiro e descendente despois podémolo facer así:

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

Produciría a seguinte saída:

<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: <xsl:key>

Podemos crear claves para identificar elementos do documento original. Os procesadores XSLT adoitan crear índices internos para as claves mellorando o acceso ao seu contido.

Para crear una clave utilízase o elemento <xsl:key>. Este elemento debe ser fillo directo de <xsl:stylesheet>. Nno se pode utilizar en ningún outro sitio, coma por exemplo formando parte dunha plantilla.

Ten tres atributos requeridos:

  • name: asigna un nome á clave para poder utilizala posteriormente
  • match: indica os nodos que serán indexados pola clave
  • use: define a propiedade dos nodos que se empregará para crear o índice. Esta propiedade é a que usaremos para acceder aos nodos do índice.

Por exemplo, para definir unha clave para os alumnos sobre o seu nome:

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

Unha vez creada a clave, utilizando a función key() obteremos os nodos da clave indicando a propiedade definida no atributo use.

No noso exemplo se posteriormente quixeramos obter a partir do nome dun alumno a nota correspondeente faríamos:

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

Identificadores de nodos

A función específica de XSLT generate-id recibe como parámetro un nodo e devolve un valor único xenerado aleatoriamente. Este valor será único en todo o documento, e ademáis será sempre o mesmo para ese nodo e esa execución: se máis adiante empregamos de novo a función generate-id co mesmo nodo obteremos o mesmo valor.

Úsase por exemplo para xenerar identificadores e enlaces internos a eles mesmos nun documento HTML:

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

Agrupamento de nodos

Gracias as claves e á xeneración de identificadores, podemos realizar o agrupamento de nodos.

Faise en dous pasos:

  • un bucle for-each selecciona o primeiro nodo de cada grupo
  • outro bucle dentro do primeiro crea os elementos do grupo

Podemos facer un grupo por cada valor da nota, e amosar dentro de cada un aos alumnos que obtuvieron esa 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>

O primeiro bucle recorre os alumnos e coa condición indicada na expresión XPath quédase soamente con uno por cada valor diferente da nota.

A función key('clave_alumno',nota) busca a nota do nodo que procesa o bucle (nota) na clave creada sobre a nota e devolve o nodo correspondente ó primeiro alumno de todos os que comparten esa nota.

Despois empregando generate-id comprobamos se o nodo que está procesando o bucle (.) é o mesmo que devolve a función key. Se non e así, non se cumple a condición (non é o primeiro nodo con esa mesma nota) e non se procesan os elementos do bucle.

O segundo bucle utiliza a clave para procesar todos os nodos cunha mesma nota.

O resultado será:

<?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: <xsl:output>

Coa etiqueta <xsl:output ...> podemos definir distintas características sobre o formato do documento de saída (son todas opcionais):

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

O atributo method e importante; se non se inclúe, o formato de saída por defecto é XML (a no ser que o elemento raíz do documento sexa html, no que a saída será HTML se non se indica outro formato).

Aínda que depende do procesador XSLT que utilicemos, na maioría das ocasións se o documento resultante é XML incluirase no mesmo de forma automática unha declaración <?xml ... ?>.

Resumo

  • Os ficheiros XML permiten organizar a información de forma xerárquica
  • Son lexibles mais hai que procesar a información para darlle un valor engadido e facilitar a comprensión
  • XSLT e un estándar para transformar documentos XML a outros documentos coma HTML ou XML
  • A etiqueta do ficheiro XML que fai referencia a folla de estilo XSL:
<?xml-stylesheet type="text/xsl" href="ficheiro.xsl"?>
  • E a orden template:
<?xsl:template match="/">
  • Distínguese a orde e o atributo match que asocia en XPath a plantilla a un nodo XML
  • As etiquetas máis usadas:
  • <xsl:value-of>. Extrae o valor dun documento XML e o engade o fluxo de saída da transformación.
  • <xsl:for-each>. Selecciona todos os elementos un a un dunha lista determinada.
  • <xsl:sort>. Ordena alfabéticamente un grupo de nodos.
  • <xsl:if>. Estrutura condicional. Executase o seu contido cando se cumpra a condición do IF
  • <xsl:choose>. Estrutura condicional. Permite optar entre varias condicións.

Exercicios

Referencias