|
Página principal del curso de XML Usando hojas de estilo para generar HTML Otros cursos y tutoriales: comercio electrónico, WAP, Webmaster Página principal del grupo GeNeura |
Para empezar habría que plantearse la necesidad de generar páginas WML a partir de XML, en vez de servirlas directamente. Y se pueden dar las mismas razones que para generar páginas HTML: XML es un formato de almacenamiento de información mucho más potente, en el cual los datos están descritos por etiquetas que indican su semántica. Además, el principal problema de los dispositivos WAP es que no son uno solo, sino muchos, cada uno con su geometría, capacidad de memoria, y capacidades: unos admiten tablas, otros no, algunos admiten WMLscript, otros no, y algunos incluso admiten Java o HTML. Es más, lo normal es que la información se quiera servir no sólo a dispositivos móviles, sino a cualquier otro tipo: navegadores en ordenadores normales, navegadores para palmtop, y que se quiera mantener una sola fuente de información.
Por eso, lo más natural es tener una sóla fuente de información, generalmente una base de datos, a partir de la cual se modelen los datos en un dialecto XML adaptado a la aplicación (noticias, cotizaciones en bolsa), y que sea un sistema de publicación XML tal como el Cocoon el que se encargue de convertir el XML al formato final, sea HTML o WML, o incluso otra forma de XML, en el caso de transacciones B2B (business to business) entre dos sistemas que entiendan XML.
En este tutorial se van a ver las nociones básicas sobre como convertir XML en WML usando las hojas de estilo XSLT. No pretende ser un tutorial completo; si quieres saber mucho más sobre el tema, consulta el tutorial de XSLT, donde se tratan de todos los medios necesarios para transformar XML. En este tutorial usaremos el Xalan 2.0, publicado en febrero del 2001, que incluye las últimas especificaciones en XSLT: un API para transformaciones sobre XML (TrAX), SAX 2, DOM level 2, y JAXP 1.0.
| Contenido de esta sección |
|---|
|
Para empezar, vamos a tratar de presentar una hoja XML que
representa una encuesta (encuesta1-wap.xml):
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="encuesta1-wap.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>
</encuesta>
Para convertirlo en WML, usaremos la siguiente hoja de estilo
(encuesta1-wap.xsl):
1 <?xml version="1.0" encoding='ISO-8859-1'?>
2 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3 <xsl:output doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml" />
5 <xsl:template match="encuesta">
7 <wml>
8 <template>
9 <do type="prev" name="prev" label="anterior">
10 <prev/>
11 </do>
12 <do type="accept" name="next" label="ultima">
13 <go href='#c2' />
14 </do>
15 </template>
16 <card id='titulo' title='Encuesta chuli'>
17 <p> <strong>Encuesta generada usando encuesta1-wap.xsl</strong><br />
18 <a href='#c1'>Comienzo encuesta</a>
19 </p>
20 </card>
21 <xsl:apply-templates />
22 </wml>
23 </xsl:template>
25 <xsl:template match="cuestion">
26 <xsl:param name='pos'><xsl:number /></xsl:param>
27 <card id='c{$pos}' title='pregunta{$pos}'>
28 <p> <b> <xsl:value-of select="pregunta"/></b> </p>
29 <xsl:apply-templates select='respuesta'/>
30 </card>
31 </xsl:template>
32
33 <xsl:template match='respuesta'>
34 <p>*<xsl:value-of select="."/></p>
35 </xsl:template>
38 </xsl:stylesheet>
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 encuesta1-wap.xml -XSL encuesta1-wap.xsl -OUT encuesta1.wml.
![]() | ![]() |
Esto debería dar como resultado algo como lo que hay en la imagen, usando el Deckit, un emulador de terminal WAP para Linux.
La primera hoja de estilo con la que nos enfrentamos es
relativamente simple. El "esqueleto" es un documento WML normal, al
cual tenemos que añadir "contenido dinámico", es decir, contenido
generado a partir del XML original. 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 (encuesta), las etiquetas
estén emparejadas correctamente y los atributos entre comillas.
Para empezar, se incluyen una serie de instrucciones, de la línea 1 a la 3: 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 (necesitamos que sea ISO-8859-1 para poder incluir acentos y demás caracteres idiosincráticos), la segunda es la etiqueta raíz de la hoja de estilo (cerrada en la última línea), mientras que la tercera el tipo de documento que necesitan los terminales WAP.
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. En este caso, el
template que corresponde a la etiqueta raíz genera un esqueleto de
baraja WML, con una tarjeta principal, que contiene un enlace a la
primera de las tarjetas. La "orden" en la línea 21 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. Lo que se hará es incluir una
tarjeta más por cada cuestion, que es lo que pretende el
template que comienza en la línea 25.
En ese template, se declara una variable en XSL: un
param, en la línea 26; y se le asigna un contador;
xsl:number devuelve un número que aumenta de valor cada
vez que se usa; lo usaremos para generar un número de pregunta, que
luego servirá también para generar atributos de cada tarjeta. El valor
de esa variable se usa en la línea 27, con el $ delante:
$pos devuelve el valor de la variable, y para evaluarlo,
tenemos dos opciones: si estamos dentro de alguna etiqueta, se rodea
por llaves (como en la línea 27), si no, se puede usar
xsl:value-of select='$variable', tal como en la línea
8. En general, xsl:value-of sirve para extraer el valor
de un camino completo dentro del árbol que forma el documento XML,
pero no nos vamos a preocupar de eso: en la línea 27 se usa para
extraer el contenido de una etiqueta que desciende de la actual:
pregunta.
En la línea 21 se aplica un template, pero de forma selectiva:
solamente a aquellos que tengan la etiqueta
respuesta. Según vayan apareciendo en el documento, se
incluirán en el documento de salida la salida generada por este
template, que empieza en la línea 33. En la línea 34 se incluye el
valor de ., es decir, el valor del contenido de la
etiqueta que se
está procesando, en este caso respuesta
Ejercicios
1. A partir de una libreta de direcciones en XML, con nombre,
dirección y teléfono, crear una baraja WML con sólo los nombres,
encerrados dentro del tag <NAME>.
| Contenido de esta sección |
|---|
|
La encuesta que se ha usado anteriormente, en realidad, es poco
operativa: habrá que incluir efectivamente los medios de que el
usuario pueda contestar a la encuesta. Este manual no va a
abarcar cómo se va a procesar la encuesta, si se quiere aprender
cómo hacerlo, es mejor mirar tutorial
de XSLT o bien el manual de CGIs desde WML.
En este manual veremos solamente cómo generar los formularios a partir
de una hoja XML que los especifique. En concreto, usaremos el
siguiente fichero
(encuesta2-wap.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>¿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='bool'>
<pregunta>¿Me quieres? </pregunta>
</cuestion>
<cuestion tipo='rollete'>
<pregunta>¿Algo más que añadir? </pregunta>
</cuestion>
</encuesta>
Este fichero usa las mismas etiquetas que el anterior, salvo que en
algunos casos, están cualificadas por el atributo "tipo": en las
"multiples" se entiende que hay que elegir una respuesta entre
varias; mientras que en las bool hay que contestar
sí o no; finalmente, en las tipo rollete hay que
escribir texto libre. Para cada uno de ellos usaremos diferentes
tipos de elementos del formulario WML: para los dos primeros
usaremos select, mientras que para la última
usaremos input. Así se hace en la siguiente
logicsheet encuesta2-wap.xsl:
14 <xsl:apply-templates select='cuestion'/>
15 </wml>
16 </xsl:template>
17
18 <xsl:template match="cuestion">
19 <xsl:param name='pos'><xsl:number /></xsl:param>
20 <card id='c{$pos}' title='pregunta{$pos}'>
21 <small>-<xsl:value-of select='$pos' />. <b> <xsl:value-of select="pregunta"/></b> <br/>
22 <xsl:choose>
23 <xsl:when test='@tipo="multiple"'>
24 <select name="rc{$pos}" title="respuestas{$pos}">
25 <xsl:apply-templates select='respuesta'/>
26 </select>
27 </xsl:when>
28
29 <xsl:when test='@tipo="rollete"'>
30 <input type='text' name='rr{$pos}'/>
31 </xsl:when>
32 <xsl:when test='@tipo="bool"'>
33 <select name="rb{$pos}" title="sino{$pos}">
34 <option value="si">Si</option>
35 <option value="no">No</option>
36 </select>
37 </xsl:when>
38 </xsl:choose>
39 </small>
40 <xsl:variable name='siguiente'><xsl:value-of select='$pos+1' /></xsl:variable>
41 <do type='accept' label='siguiente'>
42 <go href='#c{$siguiente}' />
43 </do>
![]() | ![]() |
Esto debería dar como resultado el fichero
encuesta2.wml, que se puede ver en las imágenes anteriores
usando el YoSpace, un emulador de
terminal WAP para Windows; el mejor en muchos aspectos.
El principio y el último template son exactamente igual que en la
primera logicsheet; por eso lo eliminamos y dejamos solamente
las líneas que incluyen estructuras nuevas. En concreto, a partir de
la línea 22 se usa xsl:choose, una orden que permite
incluir en la salida diferente texto dependiendo del resultado de los
tests que le siguen, que usan xsl:when test. En este
caso, lo que se prueba es el valor del atributo tipo:
@tipo, para usar diferentes elementos de formulario tal
como se ha visto anteriormente. Tal como lo requiere el estándar WML,
cada elemento tiene su nombre, generado a partir del número de
cuestion, generado como en el ejemplo anterior
Además, se usa xsl:variable en la línea 40, para
generar el número de la siguiente tarjeta, y un enlace a la misma; las
variables en XSLT son similares a los parámetros en cuanto a su uso,
pero son las únicas que se pueden usar en el cuerpo de un template;
los parámetros sólo se pueden declarar al principio de un template o
de la logicsheet. En este caso se usa para generar el número de orden
de la tarjeta siguiente, usando una operación en el atributo
select de xsl:value-of.
Ejercicios
1. Crear una baraja-anillo, es decir, una baraja de cartas en la que
cada una apunte a la siguiente, y la última a la primera. El contenido
del fichero XML puede ser cualquiera.
2. Crear una baraja de cartas que apunten unas a las otras, a partir
de un fichero XML de la
forma siguiente:
<baraja>
<carta id='carta1'>
<apunta>carta2 </apunta>
<apunta>carta4 </apunta>
</carta>. Como contenido de cada carta, aparte de
los enlaces, se pondrá el "id" de la carta.
| Contenido de esta sección |
|---|
|
A veces la información contenida en el fichero XML se debe procesar
de diferentes formas a la hora de presentarla; por ejemplo, es
habitual en datos tabulares generar primero un índice de los datos,
que aparecería como primera carta de la baraja, y luego cada uno de
los elementos de la tabla. Por ejemplo, si tenemos la clasificación
de fútbol en formato XML de la forma siguiente (futbol-wap.xml):
<?xml version="1.0" encoding='ISO-8859-1'?>
<?xml-stylesheet href="encuesta2.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>
<clasificacion>
<equipo id='ba'>
<nombre>Balompédica Alpedrete </nombre>
<gf>55 </gf>
<gc>33 </gc>
<puntos>28 </puntos>
</equipo>
<equipo id='sffc'>
<nombre>Sant Feliù FC </nombre>
<gf>44 </gf>
<gc>43 </gc>
<puntos>22 </puntos>
</equipo>
<equipo id='rp'>
<nombre>Real Polopos </nombre>
<gf>22 </gf>
<gc>77 </gc>
<puntos>3 </puntos>
</equipo>
</clasificacion>
Que se va a procesar con la siguiente logicsheet (futbol-wap.xsl):
1 <?xml version="1.0" encoding='ISO-8859-1'?>
2 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3 <xsl:output doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
4 encoding='utf-8'/>
5 <xsl:preserve-space elements='*' />
6 <xsl:template match="clasificacion">
7 <wml>
8 <template>
9 <do type="prev" name="prev" label="anterior">
10 <prev/>
11 </do>
12 <do type="accept" name="next" label="menu">
13 <go href='#principal' />
14 </do>
15 </template>
16 <card id='principal' title='El Jurbo'>
17 <p><small> <strong>Clasificación división X</strong></small></p>
18 <xsl:for-each select='equipo'>
19 <p><a href='#{@id}'><small><xsl:value-of select='nombre' /></small> </a></p>
20 </xsl:for-each>
21 </card>
22 <xsl:apply-templates select='equipo'>
23 <xsl:sort select='puntos' data-type='number'/>
24 </xsl:apply-templates>
25 </wml>
26 </xsl:template>
27
28 <xsl:template match='equipo'>
29 <card id='{@id}' title='{nombre}'>
30 <p> GF <xsl:value-of select='gf' /> <br />
31 GC <xsl:value-of select='gf' /> <br />
32 <i>Puntos</i> <xsl:value-of select='puntos' /> </p>
33 </card>
34 </xsl:template>
35
36 </xsl:stylesheet>
![]() | ![]() |
Lo cual da un resultado como el de las
imágenes (y el fichero futbol.wml).
Aparte de diferentes elementos de formateo, en esta logicsheet se
ve por primera vez la orden for-each, que es lo más
parecido a un bucle que hay en XSLT. En realidad, esta orden recorre
uno por uno cada uno de los nodos del nodeset que hay en su
atributo select. Un nodeset es un conjunto de
nodos expresados por un XPath. La forma más simple es poner el nombre
de una etiqueta, como en este caso, que dará como resultado todos los
"hijos" de la etiqueta actual que tengan esa etiqueta; pero ese XPath
se puede cualificar, por ejemplo, de la forma siguiente:
equipo[0] sería el primer hijo, y el camino completo
hasta el primer hijo se expresaría como
clasificacion/equipo/puntos, por ejemplo. Para ver una
introducción mucho más completa, consultar el tutorial
de XPath de Víctor Rivas. En resumen, el cuerpo de ese bucle se va
a repetir una vez para cada etiqueta de ese tipo, y .
contendrá el texto de la etiqueta que se está procesando
actualmente. En la línea 19 se usan además @id y
nombre, que se referirán a características (atributos y
nodos hijos) del nodo actual. En resumen, se imprimirá el nombre del
equipo, rodeado por un enlace a la tarjeta correspondiente, cuyo ID se
extrae del ID del equipo.
En cuanto al procesamiento de las tarjetas en sí, se hace a partir
de la línea 22. La principal diferencia en este caso con respecto a la
aplicación de templates que se había hecho anteriormente es que se
clasifican los equipos según su puntuación: para ello, se usa
xsl:sort; en el campo select se incluye el
XPath que se va a usar para ordenar, en este caso la puntuación
contenida en la etiqueta puntos; y en el atributo
data-type se indica el tipo de clasificación que se va a
hacer, numérica (number) o alfabética
text). Si no se especifica nada más, se ordena de mayor a
menor; pero se puede cambiar de la forma siguiente:
<xsl:sort select='puntos' data-type='number' order='descending' />
En cuanto al siguiente template, que comienza en la línea 29, lo
que hace es que incluye una carta nueva para cada equipo, usando como
ID el ID del equipo, como título el nombre, y como contenido las tres
etiquetas hijas del nodo (líneas 30-32). De hecho, esto se podría
hacer de otra forma (futbol-wap-2.xsl):
<p> <xsl:for-each select='gf|gc|puntos' >
31 <xsl:value-of select='name()' />: <xsl:value-of select='.' /> <br />
32 </xsl:for-each> </p> En
en este caso ponemos solamente las líneas que cambian. Lo que se
incluye en el atributo de for-each son diferentes
alternativas: el nodeset generado contiene a todas las
etiquetas que coincidan con alguna de las puestas (el | correspondería
a un "OR" lógico). En la linea 31, la función name()
devuelve el nombre de la etiqueta que se está procesando, mientras que
. devuelve el contenido. Así se obtiene lo visto en futbol2.wml, o en la imagen.
Ejercicios
1. A partir de una tabla HTML, crear una baraja que incluya en la
primera carta la primera columna y una columna en cada una de las otras.
Hay un par de artículos que apoyan el punto de vista de este tutorial, pero están en inglés:
No hay libros específicos sobre generación de WML a partir de XML, pero algunos libros dedican capítulos al tema:
Professional JSP : Using JavaServer Pages, Servlets, EJB, JNDI, JDBC,
XML, XSLT, and WML, de Wrox Press, que se
concentra en usar Java Server Pages para servir páginas XML,
convirtiéndolas también en WML
Java
and XML, de Brett McLaughlin, que es la Biblia de Java y
XML, con un capítulo que trata de cómo generar WML usando Cocoon. También se puede comprar en castellano: