Página principal del curso de XML
Usando hojas de estilo para generar HTML
Tutorial avanzado de generación de WML a partir de XML/XSLT
Otros cursos y tutoriales: comercio electrónico, WAP, Webmaster
Página principal del grupo GeNeura

Generación de documentos WML usando XSLT y XML

J. J. Merelo

Introducción

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
  • Ejecutando hojas de estilo
  • Hoja de estilo básica: templates
  • xsl:stylesheet, xsl:apply-templates, xsl:value-of

Hojas de estilo básicas

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>

Procesándolo con Xalan

Para procesarlo con el Xalan, tendremos que hacer lo siguiente:

  1. Añadir la máquina virtual Java al PATH: unix$ export PATH=$PATH:/usr/bin/java (o donde quiera que esté)
  2. Añadir al CLASSPATH los .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)
  3. java org.apache.xalan.xslt.Process -IN hola.xml -XSL hola.xsl -OUT encuesta1.wml.

[Hola en el Nokia WAP Toolkit]

Esto debería dar como resultado algo como lo que hay en la imagen, usando el Nokia WAP toolkit.

Procesándolo con Instant Saxon

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.

Explicación del código

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
  • Documentos XML con más etiquetas
  • Templates múltiples

Profundizando

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
  • Atributoss
  • xsl:for-each: bucles

Haciendo una baraja como Dios manda

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.

[Hola3-wap en el DeckIt][hola3-wap-2 en el
																			   DeckIt]

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
  • Procesando XML con nodos hijos de diferentes tipos
  • XPath
  • xsl:variable

Procesamiento de árboles con nodos heterogéneos

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 titulo al que corresponde el ID indicado. Eso se hace en la hoja de estilo siguiente (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>
[baraja.wml según
Gelon.net] [2ª página de la baraja en el DeckIt]

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.

Bibliografía y enlaces relacionados con XSLT

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: