Al igual que XML, XSLT es un lenguaje de programación. Forma parte de la trilogía transformadora de XML, compuesta por las CSS (Cascading Style Sheets, hojas de estilo en cascada), que permite dar una apariencia en el navegador determinada a cada una de las etiquetas XML; XSLT (XML Stylesheets Language for Transformation , o lenguaje de transformación basado en hojas de estilo); y XSL:FO, (Formatting Objects, objetos de formateo), o transformaciones para fotocomposición, o, en general, para cualquier cosa que no sea XML, como por ejemplo HTML "del viejo" o PDF (el formato de Adobe).
| XHTML sí es XML, sigue un DTD (varios, en realidad), y sólo admite documentos "bien formados". HTML no lo es, aunque pude convertirse fácilmente en XHTML usando utilidades tales como Tidy. |
XSLT es pues, un lenguaje que se usa para convertir documentos XML en otros documentos XML; puede convertir un documento XML que obedezca a un DTD a otro que obedezca otro diferente, un documento XML bien formado a otro que siga un DTD, o, lo más habitual, convertirlo a "formatos finales", tales como WML (usado en los móviles WAP) o XHTML.
Los programas XSLT están escritos en XML, y generalmente, se necesita un procesador de hojas de estilo, o stylesheet processor para procesarlas, aplicándolas a un fichero XML.
El estilo de programación con las hojas XSLT es totalmente diferente a los otros lenguajes a los que estamos acostumbrados (tales como C++ o Perl), pareciéndose más a "lenguajes" tales como el AWK, o a otros lenguajes funcionales, tales como ML o Scheme. En la práctica, eso significa dos cosas:
| SAX significa Simple API for XML; un filtro SAX trata un fichero XML como un canal (stream) sobre el cual se van aplicando transformaciones según se va leyendo. Para transformaciones simples tales como extraer un tipo de etiqueta determinada, y, de hecho, la mayoría de las incluidas en este tutorial, es mucho mejor usar un filtro SAX. El principal problema es que carecen de un lenguaje para describir esas transformaciones, y la sintaxis y aplicación depende totalmente del lenguaje en el que se trabaje. En Perl, por ejemplo, se puede usar XML::SAX, un módulo que permite hacer este tipo de transformaciones. Generalmente, en caso de ficheros muy grandes a los que haya que hacerle un procesamiento relativamente simple, SAX es una opción mejor, más rápida y con menos consumo de memoria. |
En resumen, programar con las hojas XSLT (en inglés se les llama stylesheets o logicsheets) puede ser un poco frustante, pero cuando uno aprende a usarlas, no puede vivir sin ellas. En realidad, son la única alternativa cuando uno quiere adaptar un contenido descrito con XML a diferentes clientes (por ejemplo, móviles de diferente tamaño, diferentes navegadores), y la mejor alternativa cuando uno quiere procesar documentos XML (aunque haya otras: filtros SAX, expresiones regulares...). Otra alternativa, sobre todo si se está trabajando ya con un documento XML en forma de DOM (Document object model) es trabajar directamente sobre él. En este claso, de todas formas, se pueden usar transformaciones XSL, sólo que se aplicarán en memoria, en vez de leerlas desde un fichero.
Lo que consiguen las hojas de estilo es separar la información (almacenada en un documento XML) de su presentación, usando en cada caso las transformaciones que sean necesarias para que el contenido aparezca de la forma más adecuada en el cliente. Es más, se pueden usar diferentes hojas de estilo, o incluso la misma, para presentar la información de diferentes maneras dependiendo de los deseos o de las condiciones del usuario.
Aparte del hecho habitual de procesar documentos XML, XSLT es un lenguaje de programación, y por tanto se podría hacer cualquier cosa con ellas; incluso calcular la célebre criba de Eratóstenes o ejecutar un algoritmo genético. Pero a nosotros nos va a interesar más como simple herramienta de transformación de XML.
Actualmente hay varias versiones del estándar XSLT: la versión 1.0, que es la que implementan la mayoría de los procesadores, y se denomina "recomendación", es decir, para el consorcio W3, lo equivalente a un estándar, y la versión 2.0 , que, a fecha de 4 de noviembre del 2004, es un "working draft", o borrador de trabajo, que es el paso previo a un estándar. Algunos procesadores, tales como el Saxon, implementan ya esta última versión. Hay algunas diferencias importantes: el tratamiento uniforme de los árboles (técnicamente, se pueden convertir fragmentos de árboles de resultados en nodesets), uso de múltiples documentos de salida, y funciones definidas por el usuario que se pueden definir en XSLT, y no sólo en Java u otro lenguaje, como sucedía en estándares anteriores. .
| Las convenciones que seguimos en los ejemplos son las siguientes: cada etiqueta XML en el documento XML van en diferente color, dependiendo de su posición en la jerarquía; en las hojas XSL, el código XML está en rojo, el código XSLT en verde, y el código que no es exclusivo ni de uno ni de otro, y que aparecerá tal cual en el documento final, en azul. |
Para Windows, en sus diferentes versiones, hay dos herramientas que permiten editar XML y hojas de estilo XSLT, y aplicar directamente la una a la otra. Una de ellas es XMLSpy, que tiene un IDE muy bonito, pero que casca con relativa frecuencia. De hecho, he sido incapaz de aplicar una hoja de estilo a un documento XML. Otra opción es usar eXcelon Stylus, un peaso de programa, pero que sólo está disponible para WindowsNT/2000 (y no sé si XP); la versión 3.0 beta es gratuita para desarrolladores.
En realidad, para editar XML y XSLT no hace falta ningún editor especial, pero viene bien un editor de XML o incluso un entorno integrado que te ayude a indentar bien el código, e incluso a cerrar las etiquetas en el orden correcto, o te saque la etiqueta y atributos admisibles en cada momento en función del DTD. En ese sentido, si se trabaja en Windows, el mejor es el eXcelon Stylus; en Linux, se puede uno apañar bien con el XEmacs (que te valida usando un DTD, si es necesario).
Este tutorial no pretende ser una introducción exhaustiva al XML, en realidad, para los efectos de este tutorial, hay que saber poco XML: sólo que hace falta emparejar bien las etiquetas, y la estructura de árbol que sigue un documento XML (es lo que se denomina XML bien formado). En realidad, HTML se puede definir como un documento bien formado, de hecho, la última versión de HTML, XHTML, también lo es, o sea que no es un salto grande pasar de WML/XHTML a XML. En todo caso, los que quieran saber un poco más sobre el tema, pueden consultar los diferentes tutoriales del curso GeNeura de XML.
Ejercicios
1. Crear un documento XML que describa una ficha de alumno. Tiene que
tener una etiqueta raíz (por ejemplo, acta), una
etiqueta por cada alumno, y dentro de ellas, etiquetas
para el DNI, nombre, apellidos y nota.
Hay muchas formas de usar las hojas de estilo. Lo más normal es usarlas dentro de un entorno de publicación tal como el Cocoon, o el IBM Transcoding Publisher, AxKit u otros por el estilo. Un entorno de publicación de XML permite mantener sitios completos basados en XML, y generar páginas en diferentes formatos a partir de ellos. En general, recomendamos Cocoon, que es una herramienta gratuita y Open Source basada en Java, y además una de las más avanzadas en el sector. Para una solución profesional, es mejor el IBM TP, pues forma parte del servidor de aplicaciones WebSphere, y cuenta con interfaz gráfico avanzado; el problema es el coste.
| La principal diferencia entre la versión 2 de Xalan y las anteriores es que implementa el llamado TrAX, una API para poder aplicar transformaciones a árboles XML; probablemente incorpore también la versión 1.1 de XSLT |
En muchos casos, lo que se necesita es aplicar hojas de estilo dentro de una aplicación, o usarlas desde línea de comandos a partir de otra aplicación o otro lenguaje de programación. En ese caso, lo más útil es usar un procesador de hojas de estilo, que habitualmente se encuentra en conjunción con un parser XML, con o sin validación. De estos, hay los siguientes:
xerces.jar) viene incluido en la distribución.xsltproc o el
Sablotron. Todos ellos y muchos más se pueden encontrar en la página de procesadores XSLT
de XMLSoftware.com. En Linux recomiendo el xsltproc, basado en la
librería XML/XSL de gnome; es muy rápido, es un ejecutable y
no hace falta instalar Java para usarlo.XML::XSLT para
Perl. Estos módulos se pueden usar para poder trabajar con XSLT fuera
de entornos de publicación, como por ejemplo un servidor web
normal. xalan es en realidad una librería que se puede
usar también desde programas en Java.Finalmente, para este curso, se puede usar un formulario simple que aplica hojas de estilo XSLT a documentos XML usando Perl: aplicador de hojas de estilo.
| Contenido de esta sección |
|---|
|
Para empezar, vamos a tratar de presentar una hoja XML de lo más simple (tienda0.xml):
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="tienda0.xsl" type="text/xsl"?>
<tienda>
<nombre>La tiendecilla </nombre>
<telefono>953 87 12 23 </telefono>
</tienda>
Este ejemplo lo iremos extendiendo hasta que contenga un catálogo de una tienda virtual. Por lo pronto incluimos solamente datos básicos sobre la tienda.
Para convertirlo en HTML, usaremos la siguiente hoja de estilo
(tienda-html.xsl):
1 <?xml version="1.0" encoding="UTF-8"?>
3 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
5 <xsl:template match='/'>
6 <html>
7 <head><title>Generado con tienda-html.xsl</title></head>
8 <body>
9 <h1> <xsl:apply-templates /> </h1>
10 </body>
11 </html>
12 </xsl:template>
13 </xsl:stylesheet>
Tal como está, se puede cargar directamente con Firefox, que mostrará algo similar a la imagen. Asimismo se puede usar cualquier entorno integrado que incluya la posibilidad de hacer transformaciones, como el XMLShell o XMLSpy mencionados anteriormente.
Para procesarlo con el Xalan, tendremos que hacer lo siguiente:
unix$ export PATH=$PATH:/usr/bin/java (o
donde quiera que esté).jar (ficheros de librería)
que incluyen Xerces (el parser de XML) y Xalan (el procesador de
XSLT):
unix$ export CLASSPATH=$CLASSPATH:/usr/local/xalan/bin/xalan.jar:/usr/local/xalan/xerces.jar (o
donde quiera que estén)java org.apache.xalan.xslt.Process -IN tienda0.xml -XSL tienda-html.xsl -OUT tienda.html.
Esto debería dar como resultado algo como lo que hay en esta página web.
El saxon es otro procesador de hojas de estilo, que está
disponible como un fichero .jar, y que por tanto se puede
ejecutar en cualquier plataforma. Para usarlo, se escribe (después
de definir el camino a la máquina virtual Java y al .jar
correspondiente):
java com.icl.saxon.StyleSheet tienda0.xml
tienda-html.xsl
El Instant Saxon es una versión de Saxon que ocupa poco y es relativamente fácil de usar. Hay que tener instalada una máquina virtual Java en Windows para que funcione; si tenemos instalado el Internet Explorer, con eso vamos que ardemos. Una vez instalado el explorer, basta escribir:
saxon -o tienda.html tienda0.xml tienda-html.xsl
Que nos dará el mismo resultado que anteriormente, con un poco de suerte.
También se puede usar el script en geneura, editando los ficheros anteriores y cortándolos y pegándolos en el sitio adecuado. Este script usa dos librerías de Perl: XML::LibXML y XML::LibXSLT, que a su vez están basadas en las librerías de Gnome LibXML2 equivalentes.
Para empezar, el código del documento XML contiene una etiqueta,
tienda, que incluye otras dos etiquetas,
nombre y telefono, que contienen el texto
que queremos que aparezca en la página web. En el resto, la primera
línea simplemente describe que el resto del documento es XML, y asocia
la hoja de estilo XSLT (tienda0.xsl) al documento. Esta
segunda línea se usa en entornos de publicación tales como el cocoon;
algunos procesadores de XSLT la usan para tomar el nombre de la hoja
de estilo que se va a usar; también lo suelen usar navegadores como el
Mozilla/Netscape/Firefox o el Internet Explorer. Cada una de estas
etiquetas se denominan elementos; y en principio, un
documento puede tener tantas etiquetas como queramos.
El XML original, como se ve, es simplemente XML bien formado, no
usa un DTD ni lo necesita: basta con que haya una etiqueta raíz
(tienda, en nuestro caso), las etiquetas estén
emparejadas correctamente y los atributos entre comillas. Diferentes
documentos XML podrían ser procesados con la misma hoja de estilo, y
darían diferente resultado. Ahora mismo, tal como está, un documento
XML tiene asociada una sola hoja de estilo XSLT, y para ese viaje no
hacen falta alforjas, salvo por el hecho de que muchos documentos XML
pueden tener la misma hoja XSL y de esa forma mantener una apariencia
común. También se verá más adelante la forma de que el procesador XSLT
seleccione diferentes hojas XSLT dependiendo de las circunstancias
(principalmente, el cliente).
La primera hoja de estilo con la que nos enfrentamos es relativamente simple. El "esqueleto" es un documento HTML normal, al cual tenemos que añadir "contenido dinámico", es decir, contenido generado a partir del XML original. Para hacernos una idea, en este caso simple, una hoja XSLT es como una plantilla sobre la que se cambian los contenidos por sus valores al aplicarla. En realidad, tendríamos que pensar en una hoja XSLT con en un jardinero que va podando árboles, partiendo de la raíz. Al llegar a una rama, hace algo con ella: la poda, inserta otra cosa, o simpelemente la deja tal cual; el árbol, en este caso, es realmente el árbol DOM del documento XML que está tratando.
Para empezar, se incluyen una serie de instrucciones, de la línea 1
a la 5: no son instrucciones en sí, sino que modifican el aspecto de
la salida. La primera declara el documento XML y el tipo de
codificación (que podría ser ISO-8859-1 en vez de UTF-8 si quisiéramos incluir
acentos y demás caracteres idiosincráticos) y la segunda es la etiqueta
raíz de la hoja de estilo (cerrada en la última línea; recordemos que
una hoja de estilo XSL es también un documento XML y
por tanto tiene que seguir todas sus convenciones),
que declara la versión de XSLT que se está usando y el espacio de
nombres (namespace) que vamos a usar, es decir, el prefijo
que usarán todas las instrucciones propias de XSLT; en este caso,
usamos xsl, pero cambiando esta instrucción podríamos
usar otro cualquiera.
Ese esqueleto está organizado en "templates", que son partes de la
hoja que se "disparan" cuando encuentran una etiqueta que corresponda
a lo que hay en su atributo match. El primer y único
template comienza en la línea 5, y, en este caso, el template
que corresponde a la etiqueta raíz genera un esqueleto de página
HTML. La "orden" en la línea 9 cede el control a los otros templates,
es decir, trata de aplicar todos los demás templates que haya en el
documento, incluyendo el resultado de aplicarlos precisamente en ese
punto (es decir, entre las etiquetas h1. En este caso no
hay más templates, salvo los llamados los templates por
defecto, que lo único que hacen es incluir el contenido de las
etiquetas en el documento de salida. Es decir, en la práctica lo que
hemos hecho es decirle dónde tiene que incluir en el esqueleto los
valores del documento original, sin procesarlos más.
La orden <xsl:apply-templates />
podría haberse sustituido, en este caso, por <xsl:value-of select='tienda' />
y habría tenido exactamente el mismo efecto,
es decir, incluir el contenido de la etiqueta
tienda (y todas las que descienden de ella).
Ejercicios
1. Crear un documento con otra etiqueta raíz, por ejemplo
zzzxxx, y ver qué efecto tiene en la
salida .
2. Crear un documento con varias etiquetas, con diferentes niveles de anidación, y aplicar la hoja
de estilo, para ver qué efecto tiene.
| Contenido de esta sección |
|---|
|
Vamos a ver algún documento XML algo más complicado; en concreto,
vamos a tratar de procesar documentos XML que
describen diferentes artículos de una tienda. Una
tienda virtual está formada por diferentes productos, que a su vez
tienen una serie de características: código de producto, tipo de
artículo, sección. Una tienda virtual se describiría en un fichero como el
siguiente tienda1.xml
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="tienda1-html.xsl" type="text/xsl"?>
<tienda>
<nombre>La tiendecilla </nombre>
<telefono>953 87 12 23 </telefono>
<producto>
<codigo>92 </codigo>
<cantidad>10 </cantidad>
<articulo>Radio-Casette </articulo>
</producto>
<producto>
<codigo>103 </codigo>
<cantidad>50 </cantidad>
<articulo>Reloj Cocina </articulo>
</producto>
<producto>
<codigo>1312 </codigo>
<cantidad>3 </cantidad>
<articulo>Sofá </articulo>
</producto>
</tienda>
Este se transforma con la siguiente hoja XSLT tienda1-html.xsl:
5 <xsl:template match='/'>
6 <html>
7 <xsl:apply-templates />
8 </html>
9 </xsl:template>
11 <xsl:template match='tienda'>
12 <head><title><xsl:value-of select='nombre' /> (Generado por tienda1-html.xsl)</title></head>
13 <body>
14 <h1><xsl:value-of select='nombre' /> </h1>
16 <h2>Teléfono: <xsl:value-of select='telefono' /> </h2>
18 <h2>Nuestros mejores productos </h2>
19 <table>
20 <tr><th>Código</th><th>Existencias</th><th>Artículo</th></tr>
21 <xsl:apply-templates select='producto' />
22 </table>
23 </body>
24 </xsl:template>
26 <xsl:template match='producto'>
27 <tr><xsl:apply-templates /></tr>
28 </xsl:template>
30 <xsl:template match='codigo|cantidad|articulo'>
31 <td><xsl:apply-templates /></td>
32 </xsl:template>
Esta hoja, a la que se le han suprimido los elementos comunes con la hoja anterior (comienzo y final), se compone de cuatro templates diferentes, mientras que la anterior tenía solamente uno. Cada template trata elementos de diferente profundidad dentro del árbol del documento XML: el primer template trataría la raíz, el segundo las ramas que descienden de él, y el tercer y cuarto ramas progresivamente más bajas.
Por eso el primer template simplemente incluye el código de
apertura y cierre del documento HTML, y cede control al siguiente
template, que trata la primera etiqueta, tienda, tal
como aparece en el atributo match. En este template
se incluyen dentro del patrón en HTML el nombre y al telefono, usando
xsl:value-of, que simplemente incluye el contenido
del elemento correspondiente. También se crea el esqueleto de una tabla en
HTML (líneas 19 en adelante), con su cabecera, y cede control al siguiente template, que
se encargará de cada uno de los productos; para que se procesen
sólo las etiquetas de este tipo que desciendan de
tienda, y no las otras (nombre,
telefono), que ya están incluidas en la
salida, se usa el atributo select (línea 21): con
xsl:apply-templates select='producto' se aplicarán
sólo los templates que correspondan a este elemento.
Ese template comienza en la línea 26 y crea el esqueleto de una
fila, cediendo el control a los templates que traten con los
descendientes. Hay un sólo template que lo hace, el que comienza
en la línea 30; en vez de procesar un sólo tipo de elemento,
procesa 3, y para ello usamos | para indicar que
puede procesar cualquiera de ellos, incluyéndolos dentro de un
demarcador de celda de tabla en HTML
El orden en el que se incluyen los elementos en la salida es el
mismo en el que se encuentran en la entrada, es lo que se denomina
orden de documento. Este orden se puede alterar usando
etiquetas value-of adecuadamente y usando otros
métodos, que veremos a continuación.
Ejercicios
1. Crear un documento para una clasificación de equipos de fútbol en
XML, y transformarlo mediante XSLT en una tabla HTML, con cada
equipo en una fila, diferentes valores (puntuación, goles a favor
y en contra) en columnas, con el nombre del equipo en negrita.
| Contenido de esta sección |
|---|
|
El XML es infinito, y tiene muchas más cualidades. Para empezar,
los elementos pueden tener atributos, que se añaden
dentro de la etiqueta para cualificarla <elemento
atributo1='valor1' atributo2='valor2' />; en este sentido,
funciona igual que el HTML, pero los atributos tienen que ir
siempre entre comillas (simples o dobles). De la misma forma,
cuando un elemento se repite varias veces, y hay que hacer algo
diferenciado con cada una de ellas, conviene tener a mano
bucles. Ambas estructuras las pondremos en práctica sobre el
siguiente ejemplo tiendecilla.xml
(que se muestra incompleto, porque es un rato largo):
<tienda>
<nombre>La tiendecilla </nombre>
<telefono>953 87 12 23 </telefono>
<url etiqueta="URL: ">http://www.tiendecilla.es </url>
<url prefijo="mailto:" etiqueta="Informacion: ">info@tiendecilla.es </url>
<producto>
<codigo>92 </codigo>
<cantidad>10 </cantidad>
<articulo>Radio-Casette </articulo>
<seccion>Electrónica </seccion>
<marca>Sony </marca>
<modelo>MKJ-800 </modelo>
<caracteristicas>
<linea>Auto-reverse </linea>
<linea>Dolby-sorround </linea>
<linea>Doble pletina </linea>
<linea>Ecualizador de cinco bandas </linea>
<linea>Garantía de 9 meses. </linea>
</caracteristicas>
<precio moneda="euro">90 </precio>
</producto>
<producto>
<codigo>103 </codigo>
<cantidad>50 </cantidad>
<articulo>Reloj Cocina </articulo>
<seccion>Electrónica </seccion>
<marca>Kenwood </marca>
<modelo>Blue ONE </modelo>
<caracteristicas>
<linea>Varios diseños </linea>
</caracteristicas>
<opciones nombre="color" tipo="unica">
<opcion valor="rojo"/>
<opcion valor="azul"/>
<opcion valor="blanco"/>
</opciones>
<opciones nombre="forma" tipo="unica">
<opcion valor="cuadrado"/>
<opcion valor="triangular"/>
<opcion valor="redondo"/>
<linea>Garantía de 6 meses. </linea>
</opciones>
<precio moneda="euro">12 </precio>
</producto>
<!-- Más cosas por aquí... -->
</tienda>
Este catálogo lo podemos presentar directamente como un formulario
HTML, usando la hoja de estilo siguiente: (tiendecilla-html.xsl;
sólo están las líneas que difieren de los ejemplos anteriores)
11 <xsl:template match='tienda'>
18 <xsl:for-each select='url' >
19 <xsl:value-of select='@etiqueta' />
<a href='{@prefijo}{.}'>
<xsl:value-of select='.' /></a> <br />
20 </xsl:for-each>
21 <h2>Nuestros mejores productos </h2>
22 <table border='1' borderwidth='2'>
37 <xsl:template match='caracteristicas'>
38 <td><ul>
39 <xsl:for-each select='linea'>
40 <li><xsl:value-of select='.' /></li>
41 </xsl:for-each>
42 </ul></td>
43 </xsl:template>
44
45 <xsl:template match='opciones'>
46 <td><xsl:value-of select='@nombre' /><select>
47 <xsl:for-each select='opcion'>
48 <option><xsl:value-of select='@valor' /></option>
49 </xsl:for-each>
50 </select></td>
51 </xsl:template>
52
53 <xsl:template match='precio'>
54 <td>Precio: <xsl:value-of select='.' /> <xsl:value-of select='@moneda' />s </td>
55 </xsl:template>
56
57 </xsl:stylesheet>
| Si todavía no sabes lo que es un XPath, esta es una ocasión tan buena como otra cualquiera. Se trata de una forma de expresar o seleccionar de forma única nodos o grupos de nodos de un árbol DOM que represente a un documento XML. Hay un excelente tutorial que explica qué tipos de nodos hay, y cómo expresar un nodo determinado. |
En la línea 18 (la tercera del ejemplo) aparece la primera
estructura nueva: xsl:for-each, que realiza un bucle
sobre una serie de elementos que cumplan la condición que pone
en el atributo select; en este caso, todos los
elementos url que desciendan del contexto actual
(estamos al nivel de la etiqueta tienda). En este
atributo se puede incluir cualquier XPath que dé como resultado
un node-set, y se ejecutará una vez por cada elemento
del mismo. Por ejemplo, si pusiéramos <xsl:for-each
select='producto'> se ejecutaría una vez por cada
producto, exactamente a como se hace arriba con
xsl:apply-templates (en realidad, para lo que sabemos
en este punto del tutorial, son prácticamente iguales).
Justamente dentro del bucle usamos atributos; en realidad, forman
parte de la definición de XPath y no tienen mucha historia: para
usar un atributo, se le pone la arroba delante y punto. En este
caso, se ponen las llaves alrededor para separar el atributo del
valor del elemento que se está procesando (.,
en la línea 19). Las
características y opciones se procesan de
manera análoga: se ejecuta un bucle para cada uno de los
subelementos, generando elementos de una lista en un caso, y
opciones de un formulario en otro caso.
Ejercicios
1. Hacer el ejercicio anterior, usando atributos para alguna de las cantidades.
| Contenido de esta sección |
|---|
|
Si sólo se pudieran generar páginas estáticas, poco habríamos
ganado con respecto al HTML; la gracia del binomio XML/XSLT es
que se puede generar contenido diferente dependiendo de las
entradas. Para ello debe haber alguna forma de pasarle
parámetros a las hojas, y eso se hace, cómo si no, con la orden
xsl:param. A la vez, teniendo en cuenta las entradas,
habrá que tomar decisiones; o quizás dependiendo de los
atributos. Cambiaremos la hoja de estilo anterior para que liste
sólo productos de una sección, y tomaremos decisiones sobre qué
poner dependiendo de los valores de atributos: (
tiendecilla-html-1.xsl; mostramos como de costumbre sólo
las líneas relevantes)
4
5 <xsl:param name='seccion' />
16 <h1><xsl:value-of select='nombre' /> Sección : <xsl:value-of select='$seccion' />
17 </h1>
27 <xsl:apply-templates select='producto[seccion=$seccion]' />
48 <xsl:template match='opciones'>
49 <td><xsl:value-of select='@nombre' />
50 <xsl:choose>
51 <xsl:when test='@tipo="unica"' >
52 <select>
53 <xsl:for-each select='opcion'>
54 <option><xsl:value-of select='@valor' /></option>
55 </xsl:for-each>
56 </select>
57 </xsl:when>
58 <xsl:when test='@tipo="si_no"'>
59 : <xsl:value-of select='opcion/@valor' /> <input type="checkbox" />
60 </xsl:when>
61 </xsl:choose>
62 </td>
63 </xsl:template>
Para usar esta hoja pasándole parámetros desde la línea de órdenes, habrá que hacerlo así (usando saxon):
java com.icl.saxon.StyleSheet tiendecilla.xml
tiendecilla-html-1.xsl seccion='Muebles'
o bien, usando Xalan (para más información, mirar las instrucciones
java org.apache.xalan.xslt.Process
-IN tiendecilla.xml
-XSL tiendecilla-html-1.xsl
-PARAM seccion Muebles
-OUT muebles.html
Si se usa la hoja de estilo dentro de un entorno de publicación tal
como el Cocoon, habrá que pasarle los parámetros al documento
XML, bien directamente desde el URL o desde un formulario,
algo así:
http://misitio.com/cocoon/tiendecilla.xml?seccion=Muebles. En Mozilla/Firefox y otros navegadores debería funcionar algo similar, pero no funciona. Si alguien encuentra como se hace, que avise.
En cualquier caso, el resultado será una página que incluirá solamente los productos correspondientes a una sección.
El truco para hacer esto está en los primeros templates. Para
empezar, XSLT es bastante idiosincrático, y no puede recoger
los parámetros que se le pasan a no ser que se declare un
parámetro interno, y se le asigne el nombre. Este parámetro es
similar a las variables de los lenguajes de toda la vida,
salvo que no se le puede cambiar el valor: una vez que se le
asigna uno al principio, se queda con él; el ámbito del
parámetro será toda la hoja, y mientras se esté
ejecutando. Eso se hace en la línea 5; posteriormente, en la
línea 16 se usa el valor de la variable en una sentencia
xsl:value-of; para acceder a su valor, se usa
$seccion.
En la línea 27 se usan los predicados de XPath para seleccionar
sólo aquellos nodos en los que el elemento seccion
tenga el mismo valor que la variable; en este atributo se
puede usar cualquier XPath, incluyendo este tipo de
decisiones; conocer XPath y usarlo con sabiduría nos puede
ahorrar un montón de código.
Para tomar decisiones (más que para seleccionar) un poco más
complejas, con diferentes opciones, XSLT incluye las órdenes
xsl:choose/xsl:when, que se usan conjuntamente (como
el switch/case en C), tal como se ve en la línea
53. En este caso se usa para seleccionar diferente
presentación, dependiendo del valor del atributo
tipo; en caso de que sea "unica" usamos unos menuses,
mientras que si es "si_no" usamos una "checkbox", que se pueda
pulsar o no dependiendo de la opción.
Ejercicios
1. Cambiar la clasificación, de forma que se usen sólo los equipos que
tengan una puntuación mayor que una dada, que se pase como
parámetro.
2. Cambiar la clasificación, de forma que aparezcan en una tabla los
tres primeros con un color, y los siguientes con un color
diferente. Usar expresiones XPath para seleccionarlos o
<xsl:number>; usar si es necesario una variable para
establecer el color.
| Contenido de esta sección |
|---|
|
Un documento XML puede llevar asociadas diferentes hojas de estilo, y el propio sistema de publicación (tal como el cocoon) se encargará de seleccionarla dependiendo del navegador que use el cliente. Por ejemplo, se puede añadir esta línea a un fichero XML tal como este:
<?xml-stylesheet href="encuesta1-lynx.xsl" type="text/xsl" media="lynx"?>

En este caso, Cocoon usará la hoja de estilo encuesta1-lynx.xsl cuando el navegador sea el Lynx. Esta hoja de estilo está bastante más simplificada que la anterior, dando un código HTML más simple y visible en modo texto. En el Lynx, se vería como aparece en la imagen.
¿Qué ocurre si queremos sacar los resultados por orden alfabético,
en vez del mismo orden que en el documento? Habrá que usar una nueva
opción de apply-templates, sort. Por ejemplo, si queremos
formatear el siguiente fichero XML (ordenar.xml):
<raiz>
<cosa>Pepe </cosa>
<cosa>Juan </cosa>
<cosa>Enrique </cosa>
<cosa>Xabier </cosa>
<cosa>Aarón </cosa>
</raiz>
Se puede hacer con la siguiente hoja de estilo (ordenar.xsl), de la cual
extraemos solo las órdenes pertinentes:
11 <xsl:apply-templates select='cosa'> 12 <xsl:sort select='.' data-type='text'/> 13 </xsl:apply-templates><br />
El resultado se puede ver en el fichero ordenar.htm. En este caso, en vez de aplicar la orden
apply-templates en un solo tag "vacío" (con el / al
final) se aplica en forma de tag y antitag, con las opciones en
medio. La opción xsl:sortsirve para ordenar, y como clave
de ordenación se puede usar cualquier atributo, XPath, o en este caso,
el contenido en sí de la etiqueta cosa. En la salida,
saldrán ordenados, Aarón primero y Xabier el último. El atributo
data-type sirve para indicar el criterio de ordenación,<
code>text para texto y number para ordenación
numérica.
Todavía se puede cambiar un poco más el sistema: a veces resulta
que uno quiere procesar de forma diferente un elemento,
dependiendo de la entrada. Meter decisiones puede ser un
poco coñazo, así que el propio estándar XSL tiene la forma
de hacerlo: usando modos, tal como se hace en la
siguiente y bonita hoja de estilo (ordenar2.xsl):
1 <?xml version="1.0" encoding='ISO-8859-1'?>
2
3 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4
5 <xsl:param name='modo' />
6 <xsl:output encoding='iso-8859-1' />
7 <xsl:template match='raiz'>
8 <xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction>
9 <html>
10 <head><title>Ordenando</title></head>
11 <body>
12 Mode: <xsl:value-of select='$modo' />
13 <ol>
14 <xsl:choose>
15 <xsl:when test='$modo = verbose'>
16 <xsl:apply-templates select='cosa' mode='verbose'>
17 <xsl:sort select='.' data-type='text'/>
18 </xsl:apply-templates><br />
19 </xsl:when>
20 <xsl:otherwise>
21 <xsl:apply-templates select='cosa' mode='normal'>
22 <xsl:sort select='.' data-type='text'/>
23 </xsl:apply-templates><br />
24 </xsl:otherwise>
25 </xsl:choose>
26 </ol>
27 </body>
28 </html>
29 </xsl:template>
30
31 <xsl:template match='cosa' mode='normal'>
32 <li><xsl:value-of select='.'/></li>
33 </xsl:template>
34
35 <xsl:template match='cosa' mode='verbose'>
36 <li>Nombre: <xsl:value-of select='.'/></li>
37 </xsl:template>
38
39 </xsl:stylesheet>
En esta hoja de estilo se usa el atributo mode al
llamar y al definir un template; los nombres que le hemos
asignado son arbitrarios, pero lo que hacen es que, en
caso de que se le pase como parámetro
"verbose" imprimirá "nombre" delante del
nombre de la persona; si no, lo imprimirá como antes. Para
que funcione, se toma una decisión (a partir de la línea
14): en caso de que el modo sea "verbose", se llama al
template usando como atributo de modo esa palabra; en otro
caso, se llama al template "normal". Ese código tiene el
efecto de que se llame a un template o a otro, según el
modo que se haya elegido.
Pero claro, esto se puede resolver, en este caso, de una forma
mucho más simple. ¿No sería ideal que se pudiera llamar a
un template con parámetros, como si de una subrutina se
tratara? Pues sí, se puede, tal como se hace en la hoja
siguiente (ordenar3.xsl):
6 <xsl:output encoding='iso-8859-1' />
7 <xsl:template match='raiz'>
8 <html>
9 <head><title>Ordenando 3</title></head>
10 <body>
11 Mode: <xsl:value-of select='$modo' />
12 <ol>
13 <xsl:apply-templates select='cosa'>
14 <xsl:with-param name='miModo' select='$modo' />
15 <xsl:sort select='.' data-type='text'/>
16 </xsl:apply-templates><br />
17 </ol>
18 </body>
19 </html>
20 </xsl:template>
21
22 <xsl:template match='cosa'>
23 <xsl:param name='miModo' />
24 <li><xsl:choose><xsl:when test='$miModo = "verbose"'>Nombre: </xsl:when></xsl:choose>
25 <xsl:value-of select='.'/></li>
26 </xsl:template>
27
28 </xsl:stylesheet>
La principal diferencia con los dos anteriores está en la línea 14:
la orden xsl:with-param hace que se llame al
template con un parámetro determinado, cuyo nombre y valor
queda establecido por los atributos. En este caso, estamos
asignándole al parámetro miModo el mismo valor que el del
parámetro modo (y por eso es innecesario,
peor admitámoslo por mor de la pedagogía). Ese parámetro
se recibe dentro del template con una sentencia
similar a la que se usa para los parámetros globales:
xsl:param; si el parámetro no se declara
aquí, al principio del template, XSL no le hará ni
Y ya dentro del template, usamos una decisión, como las que se han visto anteriormente, para imprimir el introito a cada nombre en caso de que el modo sea el adecuado. Y aquí paz y después gloria.
Ejercicios
1. Cambiar el programa de procesamiento del XML de los equipos de fútbol, de
forma que salgan ordenados por orden de puntuación (se
supone que el original puede estar desordenado), o por
orden alfabético, dependido de un parámetro.
2. Añadir modos a la hoja de procesamiento de las quinielas, de forma
que, en un modo, salga en forma de tabla, y en otro
modo, en forma de lista ordenada.
3. Cambiar la hoja de procesamiento de las quinielas, de forma que
cada fila aparezca con un color diferente.
| Contenido de esta sección |
|---|
|
Como era de esperar, el jefe no está contento; ya que el tema de
las quinielas está agotado, vamos a por las encuestas. Y
la encuesta hay que hacerla medianamente bien, poniendo
diferentes elementos de formularios, y no dejando que el
usuario conteste lo que le dé la gana. Usando el
fichero encuesta2.xml, habría que aplicar la siguiente hoja (encuesta3.xsl):
61 <xsl:when test='@tipo="numero"'>
62 <tr>
63 <td colspan ='2' width="100%" bgcolor="white">
64 <select name='numero'>
65 <xsl:call-template name='option-range'>
66 <xsl:with-param name='range' select='@rango'/>
67 </xsl:call-template>
68 </select>
69 </td>
70 </tr>
71 </xsl:when>
115 <xsl:template name='option-range'>
116 <xsl:param name='range' />
117 <xsl:param name='i'>0</xsl:param>
118 <xsl:if test='$range != $i'>
119 <option><xsl:value-of select='$i' /></option>
120 <xsl:call-template name='option-range'>
121 <xsl:with-param name='range' select='$range'/>
122 <xsl:with-param name='i' select='$i+1'/>
123 </xsl:call-template>
124 </xsl:if>
125 </xsl:template>
126 </xsl:stylesheet>
Como siempre, aquí está la salida. Se han puesto las líneas que introducen elementos nuevos solamente.
La principal complicación de este tipo de problemas, que se
resuelven fácilmente con un bucle for en cualquier
lenguaje de programación pachanguero, en lenguajes funcionales pueden
ser un poco más complicados. Por eso, muchos procesadores de XSL
tienen extensiones que permiten hacer bucles convencionales, como
veremos más adelante.
El principal problema es que, dado que no se pueden actualizar variables por la ausencia de efectos secundarios, no se puede crear un bucle que vaya comprobando el valor de una variable y actualizándolo en cada iteración. La única solución es sustituir los bucles clásicos por bucles recursivos, en los cuales se llama recursivamente a una subrutina (en este caso, template) hasta que se cumple una condición.
En este caso, vamos a incluir una etiqueta select y
sus correspondientes option, una para cada uno de las
opciones que haya. Y no tenemos más remedio que llamar a una rutina
recursivamente. Eso se hace en la línea 65. En este caso en vez de
dejar que los templates se disparen según los valores de una etiqueta,
lo llamamos explícitamente con la orden
xsl:call-template. Y dado que se trata de una especie de
subrutina o procedimiento, hay que llamarlo también con sus
correspondientes parámetros, para lo cual se usa
xsl:with-param. Es decir, las siguientes órdenes
<xsl:call-template name='option-range'>
<xsl:with-param name='range' select='@rango'/>
</xsl:call-template>
corresponderían en cualquier otro lenguaje de programación a
option-range( range ).
De la misma forma, la "declaración" de un template es un tanto peculiar (línea 115):
<xsl:template name='option-range'> <xsl:param name='range' /> <xsl:param name='i'>0</xsl:param>
Esto correspondería a algo así como void function
option-range(var range, var i=0), es decir, declaración de dos
variables, una de las cuales tiene un valor por defecto, el 0, y otra
sin valor por defecto, que tiene que ser dado por el usuario.
El resto de ese template, a partir de la línea 118, comprueba si el
range es distinto al contador que usamos, i, y
si lo es, imprime las etiquetas HTML, y vuelve a llamarse
recursivamente, incrementando el contador. Cuando range y
i sean iguales, la recursión para.
En este template se usan también variables, las que se pasan como parámetro. Las variables, aunque no lo son en el sentido clásico, se pueden actualizar dentro de un template, asignársele valor y pasar como parámetro a otros templates; para asignarles valor se puede usar esta construcción:
<xsl:param name='i'>0</xsl:param>
o bien con esta, que es equivalente
<xsl:param name='i' select='0' />
En ambos casos se le asignaría el valor 0 a la variable
i. Para recuperar el valor de la variable i
se usa con el $ delante, de esta forma $i. Por ejemplo,
si queremos asignar a una variable el valor de otra, se podría hacer
de dos formas:
<xsl:param name='i' select='$j'>
<xsl:param name='i'>{$j}<xsl:param>
En el segundo caso, las llaves indican que, a pesar de encontrarse
la variable fuera de una etiqueta XSL, debe ser evaluada.
Las variables y parámetros se declaran sin tipo: pueden contener
una cadena, un node-set, o un resulting tree fragment, que se puede
convertir en una cadena, pero no en un node-set; según el contexto, se
usarán como conjuntos de nodos, árboles o cadenas. La transformación a
cadenas se hace automáticamente, pero hacerlo al revés es bastante
fastidiado. Algunos procesadores incluyen extensiones que permiten
También se usa la orden xsl:if, cuyo significado es
similar a cualquier otro lenguaje. Si la condición que se le pasa en
el atributo test es cierta, se incluye en la salida el
fragmento correspondiente; si no, no se hace nada. Como no tiene nada
parecido al "else", si hay más de una opción, lo mejor es usar
xsl:when en tal caso.
En resumen, con esta hoja de estilo se obtiene una página HTML (encuesta3.html) similar a la
anterior, salvo que las respuestas de valor numérico se seleccionan a
través de un menú.
Y como evidentemente hacer las cosas así puede convertirse en un
poco pesado, algunos procesadores como el Saxon lo solucionan a base
de extensiones, es decir, etiquetas propias, tal como se muestra en el
ejemplo encuesta3-saxon.xsl. En este
caso, se tiene que añadir al principio del fichero una declaración de
espacio de nombres y de prefijo:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon">
y con esa declaración (y lo necesario para usar Saxon, que se ha
explicado anteriormente), se puede incluir el menú de la forma
siguiente:
<xsl:for-each select='saxon:range(0,@rango)'>
<option><xsl:value-of select='.' /></option>
</xsl:for-each>
Esta construcción saxon:range equivale a un bucle que se ejecuta desde 0 hasta
el número indicado en el atributo rango
(@rango). Con esto, nos ahorramos toda la historia de la
recursión y todo eso. Y el resultado es exactamente el mismo. En
realidad lo que hace es que crea un conjunto de nodos, cada uno de los
cuales tiene un valor en el rango indicado; pero qué se le va a hacer,
es la forma de hacer las cosas. El problema de usar estas extensiones
es que solamente funcionan con el procesador que las implementa, no
con el resto. Una construcción usando elementos estándar XSL
funcionará, teóricamente, en cualquier procesador. En realidad, la
orden xsl:for-each la veremos más adelante.
Ejercicios
1. Hacer un template que sume números desde 1 hasta el número
indicado. Por ejemplo, si se le llama con el 10, sumará todos los
números de 1 a 10. El número se pasará en el atributo de una etiqueta
XML. Hacerlo usando recursión primero, y luego
usando extensiones del procesador XSLT.
Contenido de esta sección
- Funciones XPath
xsl:variable
- Bucles y node-sets
El jefe ya está un poquitín más contento, la encuesta sale chuli y
tal, pero, puntilloso como siempre, nos indica que toda encuesta que
se precie tiene los numeritos de la pregunta delante de las preguntas
mismamente. De nada sirve explicarle la ausencia de bucles for (o casi
ausencia) en XSLT. Si hay que poner numeritos, hay que poner
numeritos. Además, la encuesta tal cual tiene buena pinta, pero en
realidad no sirve para nada, porque cada campo no tiene nombre propio
ni nada de eso. O sea, que hay que ponerle nombre propio, para poder
procesarlo de verdad como un formulario. Como el jefe no dice que haya
que hacerlo de verdad, ya veremos como lo haremos luego. Y hacemos la
siguiente hoja de estilo (encuesta4.xsl),
de la cual ponemos sólo los sitios que más han cambiado.:
59 <xsl:template match="cuestion">
60 <p><br/></p>
61 <table border="0" width="90%" bgcolor="#000000" cellspacing="0" cellpadding="0">
62 <tr>
63 <td width="100%">
64 <table border="0" width="100%" cellpadding="4">
65 <tr>
66 <td width="100%" colspan='2' bgcolor="#e0e0e0">
67 <big> <xsl:value-of select="pregunta"/></big>
68 </td>
69 </tr>
70 <xsl:choose>
71 <xsl:when test='@tipo="multiple"'>
72 <xsl:variable name='pos' select='position()' />
73 <xsl:variable name='varios' select='@varios' />
74 <xsl:for-each select='.//respuesta'>
75 <tr>
76 <td width="100%" bgcolor="white">
77 <xsl:value-of select='position()' />. <xsl:value-of select='.' />
78 </td>
79 <td width="100%" bgcolor="white">
80 <xsl:choose>
81 <xsl:when test='$varios=1'>
82 <input type='radio' name='radio{$pos}' />
83 </xsl:when>
84 <xsl:otherwise>
85 <input type='checkbox' name='cb{$pos}' />
86 </xsl:otherwise>
87 </xsl:choose>
88 </td>
89 </tr>
90 </xsl:for-each>
91 </xsl:when>
92 <xsl:when test='@tipo="numero"'>
93 <tr>
94 <td colspan ='2' width="100%" bgcolor="white">
95 <select name='{concat("select",position())}'>
96 <xsl:call-template name='option-range'>
97 <xsl:with-param name='range' select='@rango'/>
98 </xsl:call-template>
99 </select>
100 </td>
101 </tr>
102 </xsl:when>
103 <xsl:when test='@tipo="rollete"'>
104 <tr>
105 <td colspan='2' width="100%" bgcolor="white">
106 <input type='text' name='{concat("radio",position())}'/>
107 </td>
108 </tr>
109 </xsl:when>
110 <xsl:when test='@tipo="bool"'>
111 <tr>
112 <td bgcolor="white">
113 Si
114 </td>
115 <td bgcolor="white">
116 <input type='radio' name='{concat("radio",position())}'/>
117 </td>
118 </tr>
119 <tr>
120 <td bgcolor="white">
121 No
122 </td>
123 <td bgcolor="white">
124 <input type='radio' name='{concat("radio",position())}' />
125 </td>
126 </tr>
127 </xsl:when>
128 </xsl:choose>
129 </table>
130 </td>
131 </tr>
132 </table>
133 </xsl:template>
Los resultados se pueden ver en forma de
página.. En cualquier caso, una de las
estructuras nuevas (o casi) está en la línea 74:<xsl:for-each
select='.//respuesta'>, que introduce la orden
xsl:for-each. Esta orden es lo más parecido a un bucle
que tiene XSLT: realiza el código que le sigue (hasta el antitag
correspondiente) aplicándolo una vez por cada nodo del
node-set incluido en el atributo select. En este
caso, usa un XPath que indica que se tienen que coger todos los tags
respuesta que desciendan del tag actual (.),
sean o no descendientes directos; aunque en este caso son todos
descendientes directos; en este caso, serán todas las respuestas
correspondientes a una pregunta determinada.
Previamente al bucle, en las líneas 72 y 73, se usa
xsl:variable, que es bastante parecido a
xsl:param hasta el punto que yo no sabría
diferenciarlas... En realidad, se diferencian en cómo se les puede
asignar un valor en el caso de que sean variables globales. A una
xsl:variable se le puede asignar cualquier valor fuera de
un template, pero a un xsl:param sólo se le puede asignar
valores externos, procedentes de parámetros con los que se llama a la
página. Además, las variables no se pueden usar para pasar valores a
templates. En lo que sí se diferencian ambos de las variables
"normales", es que su valor no se puede guardar entre una invocación y
otra de un template; si se quiere conservar el valor de una variable,
hay que llamar al template explícitamente. En este caso, usamos las
variables para tomar valores que son dependientes del contexto, la
posición y un atributo, que se pierden cuando se está procesando otro
tag.
También en la línea 77 se usa una función XPath, position(),
que devuelve la posición del nodo actual dentro del contexto que se
está procesando, es decir, en este caso, el número de orden de la
respuesta actual dentro de todas las respuestas que hay.
Las dos variables declaradas, varios y
pos, se usan más adelante. varios se usa
para generar diferentes tags HTML en el caso de que se permita
respuestas múltiples (una novedad en esta hoja), o si no se permiten;
en el primer caso se usarán checkboxes, y en el segundo
botones de radio (se podía haber usado en ambos casos una combo
box con la opción múltiple o no, pero bueno...). El nombre de
los elementos del formulario se halla concatenando un prefijo tal como
"cb" o "radio" a la posición de la pregunta en el contexto,
multiplicada por dos (porque en realidad, procesamos uno de cada dos
nodos). En todo caso, consigue lo que nos interesa: que cada elemento
del formulario tenga un nombre único.
En la línea 95 se usa otra función XPath, concat, que
aunque en realidad, que nos permite concatenar la posición del nodo al
prefijo. Algo similar se usa en la línea 106. concat es
una función que devuelve como salida una cadena que concatena todo lo
que se le meta como entrada.
Ejercicios
1. A partir de un fichero XML con los equipos de fútbol tales como los
que se han usado en ejercicios anteriores, devolver otro fichero XML
que contenga sólo los equipos que tengan más goles a favor que en
contra.
2. A partir de un fichero XML con direcciones tales como los
que se han usado en ejercicios anteriores, devolver sólo aquellos cuyo
número de teléfono comience por 958. Usar la función contains.
Contenido de esta sección
- Procesando con XSL como si fuera un CGI
- Algunas extensiones Xalan para escribir ficheros, y lo mismo en
XSLT 1.1
En fin, que el jefe que dice que vale, que el formulario está
perfecto, pero a ver, tanto XML y tanta gaita gallega, pero al final
el formulario habrá que procesarlo usando PERL o algún otro lenguaje
serio. Así que, para demostrarle que no tiene razón, tendremos que
procesar también el formulario usando XSLT, aunque sea nada más que
por eso. Y lo hacemos sobre el siguiente fichero de encuesta (encuesta5.xml):
<encuesta>
<cuestion tipo='multiple'>
<pregunta>¿Eres un listo? </pregunta>
<respuesta>Si </respuesta>
<respuesta>No </respuesta>
<respuesta>Lo que diga mi señora </respuesta>
<respuesta>La gallina </respuesta>
</cuestion>
<cuestion tipo='multiple'>
<pregunta>¿Porque pierdes tu tiempo contestando encuestas? </pregunta>
<respuesta>Porque no tengo nada mejor que hacer </respuesta>
<respuesta>Porque me lo ha dicho mi señora </respuesta>
<respuesta>A tí te lo voy a decir, so listo </respuesta>
<respuesta>La gallina </respuesta>
</cuestion>
<cuestion tipo='multiple'>
<pregunta>¿Cómo considera la coyuntura actual de la mejora de las
prestaciones en orden a una europeización más completa? </pregunta>
<respuesta>Epatante </respuesta>
<respuesta>No contestaré a preguntas excluyentes </respuesta>
<respuesta>La considero positivante y conducente a unas relaciones más
normalizadas con el conjunto de los agentes socioeconómicos </respuesta>
<respuesta>La gallina </respuesta>
</cuestion>
</encuesta>
Esta encuesta la procesaremos con la siguiente hoja de estilo (encuesta5.xsl), de la cual sólo mostramos las
líneas en las que se diferencia con la anterior:
1 <?xml version="1.0" encoding='ISO-8859-1'?>
2
3 <xsl:stylesheet version="1.0"
4 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
5 xmlns:redirect='org.apache.xalan.xslt.extensions.Redirect'
6 extension-element-prefixes="redirect">
7
8 <xsl:param name='radio2'></xsl:param>
9 <xsl:param name='radio4'></xsl:param>
10 <xsl:param name='radio6'></xsl:param>
11
12 <xsl:template match='/'>
13 <xsl:choose>
14 <xsl:when test="$radio2">
15 <xsl:call-template name='procesa-encuesta'>
16 </xsl:call-template>
17 </xsl:when>
18 <xsl:otherwise>
19 <xsl:apply-templates />
20 </xsl:otherwise>
21 </xsl:choose>
22 </xsl:template>
23
24 <xsl:template name="procesa-encuesta">
25 <xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction>
26 <redirect:write select='"/tmp/encuesta.resultados.1"'>
27 Resultados:
28 <xsl:value-of select='$radio2' /> -
29 <xsl:value-of select='$radio4' /> -
30 <xsl:value-of select='$radio6' />
31 </redirect:write>
32 <html>
33 <head>
34 <title>Procesando encuesta</title>
35 </head>
36 <body>
37 <h1>Procesando encuesta</h1>
38
39 <table border="0" width="80%" bgcolor="#000000" cellspacing="0" cellpadding="0">
40 <tr>
41 <td width="100%">
42 <table border="0" width="100%" cellpadding="4">
43 <tr>
44 <td width="100%" bgcolor="#c0c0c0" align="right">
45 <big><big>Encuesta chuli</big></big>
46 </td>
47 </tr>
48 <tr>
49 <td width="100%" bgcolor="#ffffff" align="center">
50 <xsl:for-each select='.//cuestion'>
51 <table border="0" width="90%" bgcolor="#000000" cellspacing="0" cellpadding="0">
52 <tr>
53 <td width="100%">
54 <table border="0" width="100%" cellpadding="4">
55 <tr>
56 <td width="100%" bgcolor="#e0e0e0">
57 <big> <xsl:value-of select="pregunta"/></big>
58 </td>
59 <td width="100%" bgcolor="white">
60 <xsl:choose>
61 <xsl:when test='position()=1'> <xsl:value-of select='$radio2' /> </xsl:when>
62 <xsl:when test='position()=2'> <xsl:value-of select='$radio4' /> </xsl:when>
63 <xsl:when test='position()=3'> <xsl:value-of select='$radio6' /> </xsl:when>
64 </xsl:choose>
65 </td>
66 </tr>
67 </table>
68 </td>
69 </tr>
70 </table>
71 <p><br/></p>
72
73 </xsl:for-each>
74 </td>
75 </tr>
76 </table>
77 </td>
78 </tr>
79 </table>
80 </body>
81 </html>
82 </xsl:template>
83
En realidad, estamos haciendo trampa; lo cierto es que XSLT, sólo,
no puede realizar cosas tan potentes como un CGI o cualquier otro
método de procesamiento en el servidor, tales como los JSPs; XSLT está
enfocado sólo a transformaciones de documentos XML, y por eso, tenemos
que forzar un poco el estándar usando extensiones. En este caso, sería
mucho más adecuado usar XSP, por ejemplo, que permite incluir
programas en Java (u otro lenguaje) dentro de un documento XML, o
incluso las extensiones Xalan, que permiten incluir JavaScript dentro
del documento.
En este caso, si queremos generar la página usando Xalan, hay que
añadir una librería más al CLASSPATH:
export
CLASSPATH=$CLASSPATH:/usr/local/xalan-j_1_2_2/bsf.jar
(sustituyendo /usr/local/xalan... por dondequiera que
esté instalado Xalan). La librería que se añade es la que contiene el
Bean Scripting Framework, que permite usar diferentes lenguajes dentro
de la máquina virtual Java; las extensiones de Xalan están escritas
usando BSF, por eso hay que hacerlo. Y una vez hecho, se puede generar
la página siguiente (encuesta5.htm).
Por eso, para empezar, a partir de la línea 3, se tienen que
declarar las extensiones de Xalan en la declaración de la hoja de
estilo. En este caso se declara un namespace redirect,
que será el prefijo que usen las extensiones que se van a usar, y se
indica que corresponde a una clase determinada dentro del conjunto de
clases en Java que incluye Xalan. Estas extensiones se usarán más
adelante para poder escribir en disco desde una hoja de estilo.
Más adelante, en las líneas 8,9 y 10, se tienen que declarar los
parámetros del formulario que se van a usar. En este caso, el
formulario que se ha creado tiene 3 elementos: radio2, 4 y 6. A la
vez, se inicializan con un valor nulo; en la misma sentencia se le
podría asignar un valor por defecto, simplemente metiéndolo dentro de
los tags. En nuestro caso, no nos conviene, porque vamos a usarlo para
saber si el fichero XML se está procesando "de primeras" o no, o se
está procesando después de rellenar el formulario. Hay otra
alternativa a declarar esas variables, y es usar las taglibs
que vienen con el Cocoon; en concreto,
request:get-parameter-names. Una vez más, no son
universales, o sea que no van a funcionar en todos los sistemas,
aunque probablemente haya algo muy similar en el resto de los
sistemas.
Si se está usando Xalan desde la línea de comandos, se le pueden
incluir también los parámetros de la forma siguiente:
java org.apache.xalan.xslt.Process
-in encuesta5.xml -xsl encuesta5.xsl -out encuesta5-form.htm
-param radio2 3 -param radio4 5 -param radio6 3
que daría como salida un fichero HTML
(encuesta5-form.html), y a la vez crea el fichero de salida, como
veremos más adelante.
De hecho, eso es lo que se intenta dilucidar en las líneas a partir
de la 12. Esta hoja de estilo sirve a la vez para presentar la
encuesta en forma de formulario, y para procesar el formulario una vez
rellenado; y todo ello sobre el mismo documento XML y usando la misma
hoja de estilo XSLT. ¡Toma ya!
El template que comienza en la línea 12 comprueba si existe alguno
de los elementos del formulario; si existe, llama al template
procesa-encuesta, si no, aplica los templates de toda la
vida, empezando por el raíz.
Pasamos entonces al nuevo template, procesa-encuesta,
que para empezar, inserta en la salida la instrucción de siempre, que
le dice al Cocoon que tiene que servir la página como HTML, y luego
viene lo bueno: la instrucción específica de Xalan
redirect:write, que hace lo que es de esperar: escribe un
fichero con el nombre que se le pasa en el atributo
select. El nombre está entrecomillado para indicar que se
trata de una cadena, no de un XPath o algo peor; el fichero se
escribirá en el directorio /tmp, que está en todas las
distribuciones Unix con permisos de escritura. Pero, ojo, no quiero
decir que uséis esto en un entorno de producción: podría ser muy
peligroso. En tal caso sería mejor que usárais una base de datos, con
las autorizaciones adecuadas. En el fichero se escribe lo que hay
entre los dos tags, es decir, "Resultados " y el valor de los
parámetros. Saldrá algo así:
<?xml version="1.0" encoding="ISO-8859-1"?>
Resultados:
3 -
5 -
3
con su orden XML al principio y tó, tan mona. Si os dáis cuenta,
hemos tenido que hacer una verdadera chapuza: tenemos que poner de uno
en uno los valores de los parámetros; y además, solo admitimos un tipo
de parámetro, el más fácil de procesar. En realidad, lo cierto es que
XSLT no es tan potente, para este tipo de cosas, como un CGI, al menos
sin extensiones.
A continuación, a partir de la línea 50, imprimimos también cada
una de las preguntas y la respuesta correspondiente en la página HTML
que se muestra al usuario; usamos for-each y el XPath
.//cuestion, que forma un node-set con todas las
etiquetas cuestion que desciendan del nodo actual. Para
seleccionar la respuesta a esa pregunta que se va a imprimir, se hace
un truco un tanto sucio en la línea 60: con un
xsl:choose, se mira si la posición de la pregunta es la
primera, segunda o tercera, usando la función de XPath
position(), que ya hemos visto anteriormente, y se
selecciona el parámetro correspondiente. Y es una chapuza porque lo
suyo es que se carculara, pero es muy difícil convertir el contenido
de una variable en parte del nombre de un nodo, salvo que se usen
extensiones, así que lo dejamos como está.
En otros procesadores XSLT, habrá que usar otras alternativas. Y si
usamos un procesador XSL que siga la especificación XSLT 1.1, se puede
usar xsl:document de esta forma:
<xsl:document href="encuesta_resultados_s.1">
Resultados:
<xsl:value-of select='$radio2' /> -
<xsl:value-of select='$radio4' /> -
<xsl:value-of select='$radio6' />
</xsl:document>
La última versión de Saxon, usa esa orden. Probablemente, en
siguientes versiones de Xalan (y por tanto de Cocoon) se incluya
también. Para usar los parámetros desde Saxon, y obtener el
mismo resultado, hay que escribir
java com.icl.saxon.StyleSheet
-o encuesta5-saxon.html encuesta5.xml
encuesta5-saxon.xsl radio2=3 radio4=6 radio6=1
Es decir, se le pasan los parámetros de la forma
parametro=valor y ya está. El resultado es exactamente el
mismo.
En realidad, lo más adecuado sería usar XSP, JSP o un servlet para
procesarla. Pero bueno, por lo menos hemos probado lo que se puede
intentar hacer.
Ejercicios
1. Sobre cualquier documento XML, hacer una hoja de estilo que tome
como parámetro un XPath y presente ese XPath del documento original.
2. Sobre una encuesta con el formato anterior, hacer una hoja de
estilo que tenga dos parámetros, el número de pregunta y el número de
respuesta, y escribir ese número de pregunta y respuesta. Habrá que
construir un XPath a partir de los parámetros.
3. A partir de un documento XML que contenga una tabla HTML, hacer una
hoja de estilo, que, según el valor de un parámetro, escriba sólo el
valor de la primera columna o sólo el contenido de la tabla, sin
etiquetas.
Esta es la bibliografía que ha aparecido hasta el momento sobre el
tema, especialmente en castellano
- Hoja de estilos XSL, Linux Actual, Año 1, nº 15, segunda
época, p. 48, por Diego Berea y Marcos Paredes, una introducción
bastante sencilla que usa Xalan.
- Un sitio
bastante completo, con tutoriales de XSLT y de muchas otras cosas, que incluye páginas en las cuales se pueden
aplicar transformaciones XSLT sobre la marcha.
- El sitio de XSLT, que a veces no
va. Tiene todo: artículos, programas, tutoriales.
- Taller Web. Convertir XML en HTML utilizando XSLT En:
http://html.programacion.net/taller/tw_xml_y_xslt.php,
una introducción básica usando el motor XT.
- XSLT
Programmer's Reference, 2nd edition
, por Michael Kay, un peazo de libro,
completo, que sirve como tutorial y como referencia
también. Imprescindible. Además,
ahora está actualizado a la
segunda edición, incluyendo la
versión 1.1 de XSLT.
Professional XSL, de varios autores,
que es un poco más avanzado que el anterior, y está bastante bien para
aprender truquillos.
Hay también algunos tutoriales bastante buenos en inglés
- Un capítulo sobre XSLT, parte
de un la Biblia de XML. Si sólo tienes tiempo para un tutorial en
inglés, después del que estás leyendo, que sea este.
- Un sitio
bastante completo, con tutoriales de XSLT y de muchas otras cosas, que incluye páginas en las cuales se pueden
aplicar transformaciones XSLT sobre la marcha.
- El sitio de XSLT, que a veces no
va. Tiene todo: artículos, programas, tutoriales.
- El resto, lo puedes ver en una lista de punteros, con artículos, libros y tutoriales.
Este tutorial es (c) Juan J. Merelo Guervós. Se podrá reproducir en todo o en parte, siempre que se mantenga un enlace al sitio original.