|
Página principal del curso de XML Usando hojas de estilo para generar WML Otros cursos y tutoriales: comercio electrónico, WAP, Webmaster Página principal del grupo GeNeura |
Al igual que XML, XSLT es un lenguage 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; las XSLT (XML Stylesheets Transformation Language, o lenguaje de transformación basado en hojas de estilo); y las 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, 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 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 o HTML.
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++, 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:
En resumen, programar con las XSLT 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. Lo que consiguen las hojas de estilo es separar la información 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.
En realidad, XSLT son 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. Pero a nosotros nos va a interesar más como simple herramienta de transformación de XML.
Actualmente hay dos 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 1.1 , 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 pocas diferencias; la principal es que se pueden crear varios documentos de salida, en vez de uno solo.
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 este tutorial usaremos 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:
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.| Contenido de esta sección |
|---|
|
Supongamos que tenemos que dar un curso del Centro de Formación Continua, y el director del curso, que es un capullo, nos ha mandado que hagamos una encuesta para medir la satisfacción de los alumnos con el curso. Y claro, como se trata de un curso de XML, pos hay que hacerlo en XML. Para empezar, habrá que hacer en XML una descripción de la encuesta. No hace falta, en principio, que se use un DTD, basta con que sea XML bien formado. Por ejemplo, una encuesta en XML podría ser de la forma siguiente:(encuesta1.xml)
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="encuesta1.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>
<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' varios='1'>
<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>
</encuesta>
Por lo pronto, no es nada complicado: una encuesta consiste en una
serie de cuestiones, que pueden ser de diferentes tipos calificados
por el atributo tipo, y cada cuestión tiene una sola
pregunta y una o varias respuestas. Lo que nos proponemos, es, que,
por orden de nuestro jefe, a partir de ahí se genere una página HTML
que tenga, efectivamente, una encuesta. Así que, para ello, usaremos
nuestra primera hoja XSLT, que será la siguiente (encuesta1.xsl):
<?xml version="1.0" encoding='ISO-8859-1'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="encuesta">
<xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction>
<html>
<head>
<title>Encuesta, procesada con encuesta1.xsl</title>
</head>
<body>
<p><br /></p>
<center>
<table border="0" width="80%" bgcolor="#000000" cellspacing="0" cellpadding="0">
<tr>
<td width="100%">
<table border="0" width="100%" cellpadding="4">
<tr>
<td width="100%" bgcolor="#c0c0c0" align="right">
<big><big>Encuesta chuli</big></big>
</td>
</tr>
<tr>
<td width="100%" bgcolor="#ffffff" align="center">
<xsl:apply-templates/>
<p><br/></p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
<p align="center">
<font size="-1">
Copyright © 1999-2001 <a href="http://geneura.ugr.es/~jmerelo">JJ Merelo</a>.<br/>
</font>
</p>
</body>
</html>
</xsl:template>
<xsl:template match="cuestion">
<p><br/></p>
<table border="0" width="90%" bgcolor="#000000" cellspacing="0" cellpadding="0">
<tr>
<td width="100%">
<table border="0" width="100%" cellpadding="4">
<tr>
<td width="100%" bgcolor="#e0e0e0">
<big> <xsl:value-of select="pregunta"/></big>
</td>
</tr>
<xsl:apply-templates select='respuesta'/>
</table>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match='respuesta'>
<tr>
<td width="100%" bgcolor="white">
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
El resultado de aplicar esta hoja al fichero xml anterior, se puede
ver en el
servidor que usa Cocoon, o directamente en el fichero encuesta1.htm:
Para conseguir eso, se puede hacer de diferentes maneras:
[directorio/contenedor/servlets]/cocoon, en el caso del
Tomcat, que es el que
se usa en el servidor anterior, sería algo así como
/usr/local/jakarta-tomcat/webapps/cocoon/midirectorio. Además, hay que incluir las líneas
<?xml-stylesheet href="encuesta1.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>.
bash$ export CLASSPATH=$CLASSPATH:/usr/local/xalan-j_1_2_2/xalan.jar:/usr/local/xalan-j_1_2_2/xerces.jar bash$ export PATH=$PATH:/usr/local/jdk1.2.2/bin
Donde habrá que sustituir/usr/local por el directorio
donde efectivamente tengamos la JVM (en este caso
/usr/local/jdk1.2.2/bin) y hayamos descomprimido el Xalan (en este caso
/usr/local/xalan-j_1_2_2/). En caso de que estemos en
Windows, habrá que hacer las instrucciones equivalentes, algo así
como:
c:\> set CLASSPATH=%CLASSPATH%;\usr\local\xalan-j_1_2_2\xalan.jar;\usr\local\xalan-j_1_2_2\xerces.jar
c:\> set PATH=%PATH%;\usr\local\jdk1.2.2\bin
tcsh, pues de otra
forma, pero no te lo digo. ¡Cúrratelo, caray!. Una vez hecho eso, ya
se puede ejecutar el procesador sobre nuestra hoja, de esta forma:
Lo que se hace es llamar al método 'Run' del objeto 'Process'
que está situado en la jerarquía de objetos org.apache.xalan.xslt. No
hace falta saber más Java para ejecutarlo. Ese objeto está incluido en
los .jar cuyo camino hemos indicado en los mensajes
anteriores. También se puede llamar ese objeto en Java desde nuestros
propios programas en Java (si sabemos Java, claro). |
bash$ java org.apache.xalan.xslt.Process -in encuesta1.xml -xsl encuesta1.xsl -out encuesta1.htm ========= Parsing file:/home/jmerelo/public_html/XSLT/encuesta1.xsl ========== Parse of file:/home/jmerelo/public_html/XSLT/encuesta1.xsl took 1276 milliseconds ========= Parsing file:/home/jmerelo/public_html/XSLT/encuesta1.xml ========== Parse of file:/home/jmerelo/public_html/XSLT/encuesta1.xml took 103 milliseconds ============================= Transforming... transform took 108 milliseconds XSLProcessor: done
En tu caso puede tardar más o menos, pero el resultado será el mismo. El formato es el siguiente:java
org.apache.xalan.xslt.Process -in <fichero XML de entrada > -xsl
<hoja de estilo XSL > -out <fichero HTML de salida
>. Al final, todo esto da igual, porque el resultado es el
mismo de arriba.
bash$ export CLASSPATH=$CLASSPATH:/usr/local/saxon/saxon.jar
(con las salvedades correspondientes para Windows, otros shells y todo eso), y luego, sin más, ejecutarlo con el formatojava
com.icl.saxon.StyleSheet [options] <fichero XML de entrada >
<hoja de estilo XSL > [ params...], como por ejemplo:
bash$ java com.icl.saxon.StyleSheet -o encuesta1.html encuesta1.xml encuesta1.xsl
que, como era de esperar, da exactamente el mismo resultado, pero formateado más chuli, con indentación y demás.Puedes mirar como se instala un módulo
en PERL en el
tutorial de PERL. Para instalar los módulos relacionados con XML
tendrás que instalar también el parser
expat |
Bundle::XML, entre los cuales se encuentra
XML::XSLT, que sirve, precisamente, para aplicar hojas de
estilo a páginas XML. Se puede usar de la forma siguiente (appxsl.pl):
#!/usr/bin/perl use XML::XSLT; my $xslt = XML::XSLT->new ($ARGV[1], warnings => 1); $xslt->transform ($ARGV[0]); print $xslt->toString; $xslt->dispose ();
que, como es de esperar, usado de esta forma:appxsl.pl encuesta1.xml encuesta1.xsl > encuesta1-perl.htm
produce una página web exactamente igual que en los casos anteriores. En otros lenguajes de programación, tales como PHP o Ruby, o incluso Python hay también librerías y otras utilidades que permiten trabajar con XSLT. En fin, que no tienes excusa para aplicar XSLTs cuando sea necesario.A todo esto, todavía no sabemos muy bien qué es lo que hace la hoja
de estilo, y como funciona, y como da la salida a partir de la
entrada. Trataremos de analizar la hoja de estilo anterior poco a
poco. Para empezar, nos encontramos con la línea
<?xml version="1.0" encoding='ISO-8859-1'?>, que
simplemente indica que una hoja de estilo es también un fichero XML, y
por tanto tiene que ser procesado como tal; y que además, usamos el
conjunto de caracteres ISO-8859-1, que incluye caracteres latinos; si
no incluyéramos esa declaración, no podríamos usar nuestros queridos
acentos y eñes.
En la siguiente línea que dice algo, se encuentra la etiqueta raíz de todas las hojas de estilo:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Su correspondiente antietiqueta está al final del texto. En esta
declaración se incluye una declaración de espacio de nombres; indica
que se va a usar el prefijo xsl para todas las órdenes
XSLT, y entre comillas algo que parece un URL, pero que no lo es: es
simplemente un identificador único para cada espacio de nombres; de
hecho, si uno va a ese URL, no hay nada. Igual podía haberse puesto
cualquier otro prefijo, tal como xslt, pero
convencionalmente se usa siempre xsl.
A continuación, en las siguientes líneas, viene el programa XSLT en
sí. Los programas XSLT son habitualmente del tipo: regla => código a
incluir en la salida. Las reglas son los denominados
templates, o patrones. Un template se "dispara" si la
etiqueta que se encuentra en el fichero XML coincide con la que se
encuentra en su atributo match. En el caso de
<xsl:template match="encuesta">
se disparará cuando encuentre la etiqueta encuesta,
que es la etiqueta raíz del fichero de entrada, en cuyo caso
se incluirá en la salida todo lo incluido
entre esa etiqueta y su contraetiqueta, aplicándole también las
órdenes XSLT correspondientes. En este caso, se incluye una
instrucción para el cocoon:
<xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction>
para que el servidor entregue la página al cliente como un fichero
tipo html. A continuación viene simplemente código HTML
que hay que incluir en la salida, salvo la instrucción:
<xsl:apply-templates />type="text/html"</xsl:processing-instruction>
que indica al procesador que tiene que seguir aplicando templates al resto de las etiquetas del fichero de entrada. Si no se incluyera esta instrucción, el fichero de salida contendría simplemente el código HTML de este template; pero, como hay muchas más etiquetas que procesar, de esta forma se le indica que también tienen su importancia, las probeticas.
Para procesar el resto de las etiquetas hay sus templates correspondientes, que incluyen también código HTML. Pero si queremos incluir en la salida el contenido de la etiqueta, por ejemplo, lo que hay entre <pregunta> y su antitag, se usa la siguiente instrucción:
<big> <xsl:value-of select="pregunta"/></big>
<xsl:value-of select="[Etiqueta]"/> incluye en la salida el
contenido de esa etiqueta, en este caso, el contenido de la
pregunta. ¿Cómo sabe a qué pregunta se refiera? Esta instrucción tiene
en cuenta el contexto; para cada cuestion que procese,
incluirá la pregunta que descienda directamente de la
etiqueta cuestion que se esté procesando. El template
que se encarga de procesar la etiqueta cuestion, también
incluye otra orden apply-templates, para que se aplique
a todas las respuestas posibles incluidas en la etiqueta. Estas serán
procesadas con el template
<xsl:template match='respuesta'>
que simplemente incluye el valor del contenido del tag:
<xsl:value-of select="."/>
dentro de las celdas de las tablas definidas anteriormente. En este
caso, se indica . para indicar que lo que hay que incluir
es el contenido de la etiqueta que se está procesando en ese momento,
es decir, el contenido de cada una de las repuestas.
apply-templates tiene un atributo,
select, que se puede usar para seleccionar a qué
etiquetas del fichero original XML se va a aplicar los templates, o
dicho de otro modo, qué templates se van a usar a continuación. En el
fichero anterior, por ejemplo, se usa así:
<xsl:apply-templates select='respuesta'/>
En este caso se aplicarán solamente los templates que tengan
dentro de su atributo select esa condición, que en este
caso serán los que se apliquen a las etiquetas respuesta
del fichero original. El template se aplicará una vez por cada
respuesta, y los resultados saldrán en orden documental, es
decir, en el orden que se encuentran en el documento original.
Un documento XML puede llevar asociadas diferentes hojas de estilo, y el propio Cocoon se encargará de seleccionarla dependiendo del navegador que use el cliente. Por ejemplo, se puede añadir esta línea al fichero XML original:
<?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, 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 o calentito,
recién salido del Cocoon. 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,
text para texto y number para ordenación numérica.
Ejercicios
1. Hacer un fichero XML con varios equipos de 1ª división, su
nombre, goles a favor, goles en contra, y puntuación, y una hoja XSL
que formatee la hoja como una tabla, con el nombre del equipo en
negrita.
2. A partir de una libreta de direcciones en XML, con nombre,
dirección y teléfono, crear otra hoja XML con sólo los nombres,
encerrados dentro del tag <NAME>.
3. Hacer un fichero XML con datos de alumnos: apellidos, nombre y
nota; hacer dos hojas de estilo que saquen la salida en una tabla
ordenada por nota y por apellido.
| Contenido de esta sección |
|---|
|
Como el jefe del curso es un listo, dice que puede haber más tipos de preguntas que de respuesta múltiple, y que eso hay que tenerlo en cuenta. Que además, si las respuestas son de tipo Si/No, no hace falta incluirlas, sino que debe ser implícito. Así que nos ponemos a currar, y sacamos la segunda versión del fichero XML (encuesta2.xml):
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="encuesta2.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>
<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' varios='1'>
<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='numero' rango='10'>
<pregunta>Si tuvieras que calificar tu propia estupidez, ¿qué puntuación le pondrías? </pregunta>
</cuestion>
<cuestion tipo='bool'>
<pregunta>¿Me quieres? </pregunta>
</cuestion>
<cuestion tipo='rollete'>
<pregunta>¿Algo más que añadir? </pregunta>
</cuestion>
</encuesta>
con su correspondiente hoja de estilo (encuesta2.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:template match="encuesta"> 6 <xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction> 7 <html> 8 <head> 9 <title>Encuesta, procesada con <code>encuesta2.xsl</code></title> 10 </head> 11 <body> 12 13 <p><br /></p> 14 15 <center> 16 <form> 17 <table border="0" width="80%" bgcolor="#000000" cellspacing="0" cellpadding="0"> 18 <tr> 19 <td width="100%"> 20 <table border="0" width="100%" cellpadding="4"> 21 <tr> 22 <td width="100%" bgcolor="#c0c0c0" align="right"> 23 <big><big>Encuesta chuli</big></big> 24 </td> 25 </tr> 26 <tr> 27 <td width="100%" bgcolor="#ffffff" align="center"> 28 <xsl:apply-templates/> 29 <p><br/></p> 30 </td> 31 </tr> 32 </table> 33 </td> 34 </tr> 35 </table> 36 </form> 37 </center> 38 39 <p align="center"> 40 <font size="-1"> 41 Copyright © 1999-2001 <a href="http://geneura.ugr.es/~jmerelo">JJ Merelo</a>.<br/> 42 </font> 43 </p> 44 </body> 45 </html> 46 </xsl:template> 47 48 <xsl:template match="cuestion"> 49 <p><br/></p> 50 <table border="0" width="90%" bgcolor="#000000" cellspacing="0" cellpadding="0"> 51 <tr> 52 <td width="100%"> 53 <table border="0" width="100%" cellpadding="4"> 54 <tr> 55 <td width="100%" colspan='2' bgcolor="#e0e0e0"> 56 <big> <xsl:value-of select="pregunta"/></big> 57 </td> 58 </tr> 59 <xsl:choose> 60 <xsl:when test='@tipo="multiple"'><xsl:apply-templates select='respuesta'//></xsl:when> 61 <xsl:when test='@tipo="numero"'> 62 <tr> 63 <td colspan ='2' width="100%" bgcolor="white"> 64 <input type='text' /> 65 </td> 66 </tr> 67 </xsl:when> 68 <xsl:when test='@tipo="rollete"'> 69 <tr> 70 <td colspan='2' width="100%" bgcolor="white"> 71 <input type='text' /> 72 </td> 73 </tr> 74 </xsl:when> 75 <xsl:when test='@tipo="bool"'> 76 <tr> 77 <td bgcolor="white"> 78 Si 79 </td> 80 <td bgcolor="white"> 81 <input type='radio' /> 82 </td> 83 </tr> 84 <tr> 85 <td bgcolor="white"> 86 No 87 </td> 88 <td bgcolor="white"> 89 <input type='radio' /> 90 </td> 91 </tr> 92 </xsl:when> 93 </xsl:choose> 94 </table> 95 </td> 96 </tr> 97 </table> 98 </xsl:template> 99 100 <xsl:template match='respuesta'> 101 <tr> 102 <td width="100%" bgcolor="white"> 103 <xsl:value-of select="."/> 104 </td> 105 <td width="100%" bgcolor="white"> 106 <input type='checkbox' /> 107 </td> 108 </tr> 109 </xsl:template> 110 </xsl:stylesheet>
Los resultados se pueden ver en el
servidor Tomcat+Cocoon o directamente en encuesta2.html.
Aunque ya se usaban atributos en el ejemplo anterior, el atributo
multiple en la etiqueta cuestion, en este caso
tenemos varios tipos de atributos, y los contenidos de las etiquetas
tendrán que transformarse de forma diferente dependiendo del valor de
esos atributos. Hemos añadido varios tipos más: bool,
para respuestas de tipo si/no, numero, con un rango,
para respuestas que puedan tomar diferentes valores numéricos, de 0
hasta un máximo, y rollete, para respuestas en las que
se pueda contestar cualquier cosa. El tipo inicial,
multiple, tiene además otro atributo,
varios, que indica si se pueden seleccionar varias
respuestas o no. Si pretendemos generar un formulario con esto, cada
uno de ellos necesitará diferentes elementos del formulario, y
generación de diferente código de salida; de eso se encarga la hoja de
estilo.
En concreto, los cambios con respecto a la hoja de estilo anterior
comienzan a partir de la línea 59, dentro del template que procesa el
tag cuestion. En estas líneas se usan las órdenes
xsl:choose/xsl:when/xsl:otherwise, que son equivalentes
a las switch/case/default de C o C++, es decir, dependiendo del
valor de una variable, se ejecuta un código u otro. En este caso,
xsl:choose no lleva ninguna variable asociada; cada uno
de los xsl:when, a través del atributo
test, prueba cuando una condición se cumple o no. En este
caso, comprueba el valor del atributo tipo, de esta
forma
<xsl:when test='@tipo="bool"'>
El código correspondiente a esa condición se ejecutará, o mejor dicho, se incluirá en la salida si se cumple esa condición. La arroba @ se usa para referirse a atributos de la etiqueta que se esté procesando en cada momento, tal como se ve en la especificación XPath; dentro de las expresiones XSLT se puede usar cualquier expresión XPath para referirse a fragmentos o grupos de fragmentos de un documento XML. Un test similar se usa para los otros valores posibles del atributo; en cada caso se incluye un elemento de formulario diferente: botones, casillas, o incluso si/no.
Ejercicios
1. Repetir el ejercicio 1 del bloque anterior, es decir, los equipos
de la liga, pero poniendo los goles a favor y en
contra como atributos de una etiqueta principal, en vez de otra
etiqueta, por ejemplo <equipo gf='33' gc='88'
puntos='33'>.
2. A partir de un fichero con movimientos de una cuenta corriente, que
utilice como atributo si son ingresos o retiradas de fondos,
formatearlo en una tabla, poniéndolo en diferentes columnas según se
trate de un ingreso o una retirada.
| Contenido de esta sección |
|---|
|
Como era de esperar, el jefe no está contento. Si la respuesta numérica, para empezar, no se comprueba el rango, y el usuario puede responder cualquier cosa, como "Ushuaia", y se tiene que comprobar en el servidor si es correcto o no. Sabiendo el rango, y que sólo pueden ser enteros, no se podía poner un menucito con los numeros para que la gente elija, ¿eh?. Pues sí, pero, teniendo en cuenta lo que se ha dicho anteriormente de los bucles, es un tanto complicado. Usando el mismo fichero XML anterior, 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 y lo mismo, usando cocoon. Del listado anterior hemos suprimido todas las líneas que eran similares al anterior.
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 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.
| Contenido de esta sección |
|---|
|
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 o servida
por nuestro querido Cocoon. 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 |
|---|
|
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), pero
para verla actuar de veras, necesitarás
toda la potencia de Cocoon.
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
http://html.programacion.net/taller/tw_xml_y_xslt.php,
una introducción básica usando el motor XT.
Hay también algunos tutoriales bastante buenos en inglés