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 o significado. 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. Usar XML y XSLT permite dividir el contenido, descrito en XML, de la presentación, que se adaptará a cada dispositivo en particular usando XSLT.
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. El mismo sistema se puede encargar de emitir el mismo contenido en uno u otro formato, dependiendo del medio en el que se vaya a presentar la información.
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. Alternativamente, se puede usar otro procesador XSLT, tal como el Saxon, que es mucho más fácil de usar en Windows.
| 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).
En todo caso, estos entornos sirven como desarrollo; para implementar un sitio WAP basado en XML y XSLT hace falta una plataforma profesional, tal como el IBM Transcoding Publisher, o un servidor basado en servlets tal como el Cocoon. En este tutorial sólo se dan nociones de cómo generar WML a partir de XML y XSLT; en el caso de cada servidor se aplicarán de forma diferente.
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. 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.
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. En realidad, WML está ya definido como un documento XML, y 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.
| Contenido de esta sección |
|---|
|
Para empezar, vamos a tratar de presentar una hoja XML de lo más simple (hola.xml):
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="hola.xsl" type="text/xsl"?>
<tarjeta>
Hola
</tarjeta>
Para convertirlo en WML, usaremos la siguiente hoja de estilo
(hola.xsl):
1 <?xml version="1.0" encoding="UTF-8"?>
2 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3 <xsl:output
4 doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
5 indent='yes'/>
6 <xsl:template match='/'>
7 <wml>
8 <template>
10 <do type="prev"><prev/></do>
11 </template>
12 <card id="card1" title="Card #1" newcontext="true">
16 <p align="center">
17 <big><b><xsl:apply-templates /></b></big>
18 </p>
19 </card>
20 </wml>
21 </xsl:template>
22 </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 hola.xml -XSL hola.xsl -OUT encuesta1.wml.
Esto debería dar como resultado algo como lo que hay en la imagen, usando el Nokia WAP toolkit.
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 hola.wml hola.xml hola.xsl
Que nos dará el mismo resultado que anteriormente, con un poco de suerte.
Para empezar, el código XML contiene una simple etiqueta,
tarjeta, que contiene el texto que
queremos que aparezca en el móvil. En el resto, la
primera línea simplemente describe que el resto del
documento es XML, y asocia la hoja de estilo XSLT
(hola.xsl) al documento.
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 (tarjata), 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. Para hacernos una idea, en este
caso simple, una hoja XSLT es como una plantilla sobre
la que se cambian los contenidos.
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), 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), mientras que
la tercera a quinta indican que la salida es el tipo de documento que
necesitan los terminales WAP, y que además, salga
indentado (indent='yes') para que salga
chuli.
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 6, y, n este caso, el
template que corresponde a la etiqueta raíz genera un esqueleto de
baraja WML, con una tarjeta principal. La "orden" en la línea 17 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. 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 la etiqueta raíz, que en este caso es
tarjeta 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.
La orden <xsl:apply-templates />
podría haberse sustituido por <xsl:value-of select='tarjeta' /> y habría tenido exactamente el mismo efecto, en este caso,
es decir, incluir el contenido de la etiqueta
tarjeta. Pero no funcionaría si hubiera
otras etiquetas más dentro de esa.
Ejercicios
1. Crear un documento con otra etiqueta raíz, por ejemplo
zzzxxx, y ver qué efecto tiene en la
salida .
| Contenido de esta sección |
|---|
|
En este apartado, vamos a intentar procesar un código XML un poco
más complejo, y generar una baraja WML con varias
cartas a partir de él. éste sería el código (hola2.xml):
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet href="hola2.xsl" type="text/xsl"?>
<baraja>
<tarjeta>
Esta es lo de la primera
</tarjeta>
<tarjeta>
Y esta es la segunda
</tarjeta>
</baraja>
Este fichero tiene la estructura en árbol que aparece en la figura
adjunta; es decir, una etiqueta raíz, que
en este caso es baraja, y varias
etiquetas que cuelgan de esa raíz,
tarjeta, aparte de varias cosas más
que no vienen a cuento.
Y ésta sería la hoja de estilo asociada:(hola2.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
4 doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
5 indent='yes'/>
6 <xsl:template match='/'>
7 <wml>
8 <template>
9 <!-- Template implementation here. -->
10 <do type="prev"><prev/></do>
11 </template>
12 <xsl:apply-templates />
13
14 </wml>
15 </xsl:template>
16
17 <xsl:template match='tarjeta'>
18 <card>
19 <p align="center">
20 <big><b><xsl:apply-templates /></b></big>
21 </p>
22 </card>
23 </xsl:template>
24 </xsl:stylesheet>
En este caso, la hoja de estilo tiene que recorrer el árbol, de la raíz a las hojas, para sustituir dentro del esqueleto que hay en la plantilla XSLT los valores correspondientes del documento XML. Por eso hay dos templtes, uno para la raíz y otro para las hojas; en caso de que hubiera más hojas, u hojas que tuvieran que procesarse de forma diferente, habría que hacer más templates; en este caso con 2 vamos que ardemos.
Como se trata de dos tarjetas (contenidas cada una en
su etiqueta), vamos a hacer una
baraja con sendas card, o, más
precisamente, una card por cada
etiqueta. Los dos
templates corresponderán a dos
"esqueletos" o "plantillas", una para la
estructura general de la baraja y otra por cada
tarjeta.
En el primer template, que comienza en la línea 6, se colola la
estructura general de la baraja: un
template WML con código para volver
a la tarjeta anterior. Posteriormente, en la
línea 12, se cede control al resto de los
templates, es decir, se siguen colocando las
estructuras correspondientes a las hojas. En
este punto, lo que se hará es que, por cada nodo
que descienda del nodo raíz, se "llamará" una
vez al template siguiente, el que procesa cada
tarjeta.
El template que procesa cada tarjeta comienza en la línea 17. Para
indicar que se van a procesar sólo los nodos de
tipo tarjeta, es decir, los que
tienen una etiqueta con ese nombre, se pone como
atributo del elemento
match=tarjeta; si hay un nodo hijo
de algún otro tipo, no lo procesa. Este template
contiene la plantilla de una tarjeta, e incluye
como texto de la tarjeta el contenido del
nodo. Para ello se usa, como en el ejemplo
anterior, apply-templates. Como en
el caso anterior, se podía usar
value-of, pero sería menos general,
y dejaría de procesar nodos inferiores.
El resultado, presentado en el Nokia WAP Toolkit, es el que se puede aprecier en la imagen.
Ejercicios
1. Crear una vez más documento con otra etiqueta raíz, por ejemplo
zzzxxx, y ver qué efecto tiene en la
salida.
2. Para un documento XML que contenga varias personas, y de cada
persona, el nombre y el apellido cada
uno en su etiqueta, generar una baraja
con una persona en cada tarjeta, los
apellidos en negrita, y el nombre en
letra normal.
| Contenido de esta sección |
|---|
|
Tal como está puesto anteriormente, la baraja WML no sirve de
mucho, porque sólo se puede acceder a la
primera tarjeta, no hay forma de acceder a
la segunda desde la primera. Por eso, vamos
a intentar hacer una baraja un poco más
navegable. Pero para ello necesitamos
cambiar ligeramente el código XML anterior
(hola3.xml):
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet href="hola3.xsl" type="text/xsl"?>
<baraja>
<tarjeta id='t1' titulo='primera'>
Esta es lo de la primera
</tarjeta>
<tarjeta id='t2' titulo='segunda'>
Y esta es la segunda
</tarjeta>
</baraja>
La principal diferencia es que hemos incluido dos
atributos en cada
tarjeta, id y
titulo, que vamos a usar para
asignar un ID y un título a cada tarjeta que
generemos, y así poder navegar por
ellas. Para generar el WML, usamos la
siguiente hoja de estilo XSLT (hola3.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
4 doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
5 indent='yes'/>
6 <xsl:template match='/'>
7 <wml>
8 <template>
9 <!-- Template implementation here. -->
10 <do type="prev"><prev/></do>
11 <do type="Indice"><go href='#indice' /> </do>
12 </template>
13
14 <card id='indice' title='Indice'>
15 <p align="center">
16 <xsl:for-each select='baraja/tarjeta'>
17 *<a href='#{@id}'>Tarjeta <xsl:value-of select='@titulo' /> </a> <br />
18 </xsl:for-each>
19 </p>
20 </card>
21
22 <xsl:apply-templates />
23 </wml>
24 </xsl:template>
25
26 <xsl:template match='tarjeta'>
27 <card id='{@id}' title='{@titulo}'>
28 <p align="center">
29 <big><b><xsl:value-of select='.' /></b></big>
30 </p>
31 </card>
32 </xsl:template>
33 </xsl:stylesheet>
En esta hoja de estilo seguimos usando dos templates, como en el
caso anterior, uno para la raíz y otro para
las hojas, pero además tenemos que generar
una tarjeta índice, y usar los atributos de
las tarjetas en las
card que vamos a generar. Por
eso, aparte de pequeños cambios en el
template de la baraja, introducimos
a partir de la línea 14 el código de la
tarjeta índice. Este código contiene un
bucle (línea 16); este bucle se ejecuta una
vez para cada nodo que sea una tarjeta, eso
es lo que se indica con
select='baraja/tarjeta'. Hay
que indicar el camino completo
(XPath) hasta las etiquetas que
queremos procesar; la parte interior del
bucle se ejecutará una vez por cada nodo de
ese tipo; y lo que hará será incluir el
texto correspondiente en el documento de
salida.
El texto incluye referencia a los dos atributos de cada tarjeta: el
id y el titulo; en
XSLT, para referirnos a atributos, usamos
@, por tanto,
<xsl:value-of select='@titulo'
/> se convertirá, para la
primera tarjeta, en primera,
que es el contenido de ese atributo para la
primera tarjeta. Usamos otra forma para
incluir los atributos, si nos encontramos ya
dentro de una etiqueta: {@id};
de esta forma incluimos un atributo de un
elemento del documento XML original como
atributo del documento XML
resultante.
![]() | ![]() |
El tipo de bucle que nos encontramos aquí es lo más que nos vamos a
encontrar en XSLT: es un lenguaje funcional,
y no hay ningún bucle for ni
while ni nada por el
estilo. Pero con este nos apañamos por el
momento.
El segundo template, que empieza en la línea 24, también ha sido
modificado con respecto al ejemplo anterior:
la card tiene ahora los
atributos de id y titulo, cogidos del
documento XML original, y como contenido de
la tarjeta, se usa la orden <xsl:value-of select='.'
/>, es decir, el contenido en
texto de la etiqueta que se está
procesando. El resultado es el que se
muestra en la imagen (procesada con el DeckIt: una página de índice,
tó chula, con su título y tó, y otras dos
páginas, también puedes agarrar el código de
hola3.wml. Con el botón Back y con
los enlaces se puede navegar entre las
diferentes páginas.
| Contenido de esta sección |
|---|
|
Todavía, la generación del documento deja algo que desear, porque
para ir de una tarjeta a otra hay que pasar por la tarjeta
índice; lo más natural sería que hubiera enlaces de cada tarjeta
a las que estuvieran relacionadas con él. Estas relaciones entre
tarjetas las vamos a expresar con una etiqueta llamada, de forma
original, enlace, en el siguiente documento XML (baraja.xml):
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet href="baraja.xsl" type="text/xsl"?>
<baraja>
<tarjeta id='t1' titulo='primera'>
Esta es lo de la primera
<enlace con='t2' />
</tarjeta>
<tarjeta id='t2' titulo='segunda'>
Vamos con la segunda, que es la más interesante
<enlace con='t1' />
<enlace con='t3' />
</tarjeta>
<tarjeta id='t3' titulo='tercera'>
Última de todas
<enlace con='t2' />
</tarjeta>
</baraja>
Cada etiqueta enlace contiene un atributo
con que indica el ID de la tarjeta a la cuál se va a
enlazar. Lo que nos gustaría es, a partir de esa etiqueta, generar un
enlace en WML que contenga como texto el título de la tarjeta, es
decir, que deberíamos "calcular", usando XSLT, el atributo
baraja.xsl), que es una evolución de la
anterior.:
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
4 doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"
5 indent='yes'/>
6 <xsl:template match='/'>
7 <wml>
8 <template>
9 <!-- Template implementation here. -->
10 <do type="prev"><prev/></do>
11 <do type="Indice"><go href='#indice' /> </do>
12 </template>
13
14 <card id='indice' title='Indice'>
15 <p align="center">
16 <xsl:for-each select='baraja/tarjeta'>
17 *<a href='#{@id}'>Tarjeta <xsl:value-of select='@titulo' /> </a> <br />
18 </xsl:for-each>
19 </p>
20
21 </card>
22
23 <xsl:apply-templates />
24 </wml>
25 </xsl:template>
26
27 <xsl:template match='tarjeta'>
28 <card id='{@id}' title='{@titulo}'>
29 <p align="center">
30 <big><b><xsl:value-of select='texto' /></b></big> <br />
31 <xsl:apply-templates select='enlace' />
32
33 </p>
34 </card>
35 </xsl:template>
36
37 <xsl:template match='enlace'>
38 <xsl:variable name='con' > <xsl:value-of select='@con' /> </xsl:variable>
39 *<a href='#{$con}'>Tarjeta <xsl:value-of select='/baraja/tarjeta[@id = $con]/@titulo' /> </a> <br />
40 </xsl:template>
41 </xsl:stylesheet>
![]() |
![]() |
Las principales diferencias con respecto al ejemplo anterior se encuentran a partir de la línea 31: aplicamos los templates una vez más, recursivamente. El orimer template se aplicaría al primer nivel del árbol, el que hay más a la izquierda en la imagen, mientras que el segundo se aplicaría al segundo nivel, y el último template en el fichero al último nivel.
Como en el tercer nivel hay dos tipos de nodos, los nodos tipo
texto, y los nodos tipo enlace, en la línea
31 especificamos que los únicos templates que se tienen que aplicar
son los que tengan en su campo match la palabra
enlace, no los que tengan texto, que para
eso ya los hemos procesado antes. Si no lo hiciéramos así, se
aplicarían los templates relativos a ambos tipos de nodos; y aunque no
hay ningún template declarado para procesar los nodos
texto, hay un template por defecto en cualquier hoja de
estilo, que simplemente incluye en el documento de salida el texto
contenido en el enlace. En resumen, se aplica el template para los
enlaces, que comienza en la línea 37.
Este template lo que hace es lo siguiente: para empezar, declara
una variable llamada con, usando la
sentencia xsl:variable (línea 38); las
variables en XSLT son un tanto peculiares,
más parecedias a constantes; una vez
asignado un valor, se puede comparar, y
operar con él, pero no se puede cambiar el
valor (salvo que usemos extensiones
particulares de algún procesador). La
variable se usa en la línea siguiente: se
crea un enlace a la tarjeta cuyo ID está
contenido en la variable *<a href='#{$con}'>, y luego se incluye, como
contenido del enlace, el título de la tarjeta cuyo id corresponde al
ID almacenado en la variable
$con. Eso es lo que se quiere
decir con /baraja/tarjeta[@id =
$con]/@titulo. Leído de izquierda a
derecha, querría decir: "Escoge una tarjeta
de la baraja cuyo atributo id sea igual al
ID almacenado en la variable $con. De esa
tarjeta, selecciona el título". Esto es un
XPath un poco más complicado de lo que hemos
visto anteriormente. Si se quiere saber un
poco más sobre el tema, hay un tutorial de
XPath bastante completo en el Tutorial de Xpath de Víctor Rivas.
En resumen, se generarán una serie de tarjetas, cada una con
enlaces a las demás tarjetas indicadas, tal
como aparece en el fichero baraja.wml.
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>.
2. A partir de un documento XML que describa equipos de fútbol de esta
forma:
<liga>
<equipo>
<nombre>....</nombre><puntos>...</puntos>
</equipo>
</liga>
generar una baraja WML con un índice que contenga los nombres de los
equipos y los datos de cada equipo en cada
carta, con el título de la carta igual al
nombre del equipo.
Hay un par de artículos que apoyan el punto de vista de este tutorial (es decir, usar XML para generar WML en vez de escribir WML a pelo), 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.