I.E.S. Suárez de Figueroa @vanza C.F.G.S. Administración de Sistemas Informáticos en Red

Anuncio
I.E.S. Suárez de Figueroa
@vanza
C.F.G.S. Administración de Sistemas Informáticos en Red
Módulo "Lenguaje de Marcas y Sistemas de Gestión de la Información"
Unidad 5. Conversión y adaptación de documentos XML.
1. Técnicas de acceso y transformación de documentos XML.
Como ya sabemos, el lenguaje de marcas XML aplica una estructura a un conjunto de
datos, generando un documento estructurado; por otra parte los lenguajes de marcas HTML y
XHTML generan documentos con formato.
Existen otros lenguajes de marcas que permiten acceder a la información de
documentos XML y aplicar un formato claro y atractivo para publicarlo en páginas web.
Además, también se ofrece la posibilidad de generar informes en formato PDF.
El lenguaje de marcas XPath permite acceder a los datos de un documento XML,
mientras que el lenguaje de marcas XSLT permite aplicar transformaciones a los documentos
XML.
Los documentos XML son documentos de texto con etiquetas que contienen
exclusivamente información con una estructura, sin entrar en detalles del formato. Esto
implica la necesidad de algún medio para expresar el acceso y la transformación de un
documento XML, para que una persona pueda utilizar directamente los datos para leer,
imprimir, etc. Por tanto, a partir de un documento XML podremos obtener otro documento
XML, un documento HTML (o XHTML), un documento de texto, e incluso un documento PDF.
Las tecnologías que entran en juego en el acceso y transformación de documentos son:

XPath: permite el acceso a los diversos componentes de un documento XML

XSLT: permite definir el modo de transformar un documento XML en otro.

XSL-FO: Se utiliza para transformar XML en un formato legible e imprimible por una
persona, por ejemplo en un documento PDF.
2. XPath.
XPath es un lenguaje que permite separar la información de los documentos XML de
las etiquetas de los mismos.
XPath permite acceder (o referenciar) a las partes o nodos de un fichero XML; o dicho
de otro modo, XPath permite identificar y seleccionar elementos o nodos del documento XML.
Una expresión XPath se aplica sobre una estructura de árbol correspondiente a la
jerarquía de los datos de un documento XML, y devuelve todo lo que encaja con esa expresión.
Es un estándar aprobado por el W3C, que nos permite acceder a partes de un
documento XML basándonos en las relaciones de parentesco entre los nodos (elementos) del
documento. Su notación es similar a las de las rutas de los ficheros. Se puede decir que XPath
es un lenguaje XML, pero realmente lo que hace es basarse en la estructura jerárquica de los
documentos XML.
Inicialmente se creó para utilizarlo con XSLT, pero en la actualidad se utiliza también
con XML Schema, Xquery, Xlink, Xpointer, Xforms, etc.
En https://www.w3.org/TR/XPath/ puedes encontrar el estándar de XPath que aprobó
el W3C el 19 de Noviembre de 1999 y sobre el que sigue aplicando revisiones en la actualidad.
2.1. Términos básicos.
Existen algunos términos que se deben conocerse para utilizar XPath.
XPath considera un documento XML (con sus elementos, atributos y datos) como un
árbol de nodos. Una expresión XPath es una cadena de texto que representa un recorrido en el
árbol del documento, que está formado por nodos.
Veamos los distintos tipos de nodos:

Nodo raíz, es el nodo que contiene al ejemplar o elemento raíz del fichero XML; no es
el ejemplar, sino el que lo contiene; el ejemplar o elemento raíz del XML está por
debajo del nodo raíz del XPath. Se identifica por “/”. Por ejemplo, si un documento
tiene el ejemplar o elemento raíz <alumno>, el nodo raíz es / y para acceder al
ejemplar se forma la expresión /alumno de XPath. Se puede decir por tanto que el
elemento padre del elemento raíz es el nodo raíz, por lo que todos los elementos de
un XML tiene elemento padre, incluido el elemento raíz.

Nodos elemento, son cada uno de los elementos del documento XML. Todos ellos
tienen un elemento padre, que es el elemento al que pertenece; el padre del elemento
raíz, es decir del ejemplar, es el nodo raíz del documento. Por ejemplo:
<alumno>
<nombre>Juan</nombre>
<fecha_nac>
<dia>21</dia>
<mes>11</mes>
<anio>1990</anio>
</fecha_nac>
</alumno>
Nodo raíz: /
Es el padre del elemento alumno
Nodo alumno: /alumno
Es el padre de los elementos nombre y fecha_nac
Nodo nombre: /alumno/nombre
Nodo fecha_nac: /alumno/fecha_nac
Como vemos, para separar un nodo (alumno) del nodo hijo (nombre) se usa la / (el
mismo símbolo que se utiliza para el nodo raíz).

Nodos texto, son los propios datos del documento XML. Por tanto, estos nodos texto
no tienen hijos. Por ejemplo, en el código anterior son nodos texto "Juan", "21", "11" y
"1990".

Nodos atributo, no se consideran hijos del elemento al que están asociados sino
etiquetas añadidas al nodo elemento.

Nodos de comentario, son los nodos que se generan para los elementos con
comentarios del documento XML.

Nodo actual, es el nodo sobre el que se evalúa una expresión XPath.

Nodo contexto, cada expresión XPath está formada por subexpresiones que se van
evaluando antes de resolver la siguiente. El conjunto de nodos obtenido tras evaluar
una expresión y que se utiliza para evaluar la siguiente es el nuevo contexto.

Tamaño del contexto, es el número de nodos que se están evaluando en un momento
dado en una expresión XPath.
Las expresiones XPath nos permiten obtener cuatro tipos de resultados diferentes:




Un conjunto de nodos.
Un valor booleano o lógico (valores verdadero o falso).
Un valor numérico, pero no un conjunto de valores numéricos.
Una cadena de caracteres, pero no un conjunto de cadenas.
Una ruta de localización se corresponde con la ruta que hay que seguir en un árbol
para localizar un nodo. Las rutas de localización siempre devuelven el conjunto de nodos del
árbol XML sobre el que se aplican que cumplen una condición XPath dada. Dicho conjunto
podría estar vacío.
2.2. Selección de nodos.
Para comprobar los resultados de las sentencias o expresiones de XPath se puede
utilizar el programa "XML Copy Editor". Teniendo el fichero .xml abierto, entrando en la opción
del menú superior "XML - Evaluar XPath" (o directamente pulsando la tecla F9) se abre una
pequeña ventana donde se escribe la expresión o sentencia XPath; al ejecutarla se muestra el
resultado.
En los ejemplos posteriores usaremos el siguiente fichero XML de agenda:
<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<agenda>
<propietario>
<nombre>Ana</nombre>
<apellidos>Pinto Ramos</apellidos>
</propietario>
<contactos>
<persona id="p01">
<identificadores>
<nombre>Maribel</nombre>
<apellidos>Lago Santana</apellidos>
</identificadores>
<direccion>
<calle>Mayor 22, 2B</calle>
<localidad>Badajoz</localidad>
<cp>06002</cp>
</direccion>
<telefonos>
<movil>924111111</movil>
</telefonos>
</persona>
<persona id="p02">
<identificadores>
<nombre>Roberto</nombre>
<apellidos>Ramos Juan</apellidos>
</identificadores>
<direccion>
<calle>La Central 41, 4C</calle>
<localidad>Zafra</localidad>
<cp>06300</cp>
</direccion>
<telefonos>
<movil>666222222</movil>
<casa>924222222</casa>
</telefonos>
</persona>
<persona id="p03">
<identificadores>
<nombre>Juan</nombre>
<apellidos>Lima Ruz</apellidos>
</identificadores>
<direccion>
<calle>Huertos 18</calle>
<localidad>Badajoz</localidad>
<cp>06004</cp>
</direccion>
<telefonos>
<movil>666333333</movil>
<casa>924333333</casa>
<trabajo>924343434</trabajo>
</telefonos>
</persona>
</contactos>
</agenda>
Algunas notaciones en XPath son:
- La barra / (es decir, la barra que está en la tecla del 7) es el nodo raíz; como ya hemos
indicado antes, este nodo raíz es el padre del ejemplar del XML, o lo que es lo mismo, el
ejemplar es hijo de la barra /.
Por ejemplo:
Ejemplar XML:
Notación XPath:
<agenda>
/agenda
Como vemos, para referenciar (acceder) el elemento raíz o ejemplar del XML se usa el
nodo raíz (la barra /) y el nombre del ejemplar.
Si probamos esa expresión en XML Copy Editor, teniendo el documento .xml abierto
que contiene la agenda, comprobaremos que el resultado es la agenda con todos sus
elementos o nodos hijos; es decir, muestra todo el documento .xml, queda seleccionado el
documento completo.
- Para referenciar o acceder a un elemento del XML se debe comenzar por el nodo raíz hasta
llegar al elemento en cuestión, según el árbol del fichero XML; por ejemplo:
XPath para el nodo "propietario":
/agenda/propietario
Esta expresión aplicada al XML anterior selecciona lo siguiente:
<propietario>
<nombre>Ana</nombre>
<apellidos>Pinto Ramos</apellidos>
</propietario>
Observamos que cuando se selecciona un nodo, también se incluye su contenido (sus
nodos hijos y atributos); en este caso queda seleccionado todo lo que contiene "propietario".
XPath para el nodo "persona": /agenda/contactos/persona
Esta expresión selecciona todos los nodos "persona" (las tres personas) incluyendo
todo su contenido.
Como vemos, para acceder a un nodo se escriben los sucesivos elementos o nodos
separados por la barra (la misma que se usa para indicar nodo raíz), hasta llegar al elemento en
cuestión; se comienza por el nodo raíz (la barra / inicial) seguido del ejemplar del XML (agenda
en este caso).
Se puede deducir que la / se usa para acceder al hijo de un nodo; "persona" es hijo del
nodo "contactos", por eso se usa "contactos/persona" para acceder a "persona". También
existe la notación child:: en lugar de la barra (realmente es /child::), pero lógicamente es más
sencillo usar sólo la barra:
/agenda/contactos
es equivalente a
/agenda/child::contactos
- Para referenciar o acceder a un atributo de un elemento se utiliza la arroba @, después de la
barra / por ejemplo:
XPath: /agenda/contactos/persona/@id
Esta expresión realiza la siguiente selección:
id="p01"
id="p02"
id="p03"
Es decir, los identificadores id de las tres personas de la agenda.
La @ se corresponde con attribute:: por lo que la expresión anterior podría quedar:
XPath: /agenda/contactos/persona/attribute::id
Lógicamente es más sencillo y rápido usar la @.
- Las dos barras // permiten seleccionar nodos de cualquier parte del documento, sin
importar donde se encuentran, es decir no tiene en cuenta cual es el nodo padre. Equivale a
descendant:: (realmente equivale a /descendant::) por lo que es lo mismo escribir
/descendant:: que // pero lógicamente es más sencillo //.
XPath: //nombre o /descendant::nombre
Esta expresión selecciona:
<nombre>Ana</nombre>
<nombre>Maribel</nombre>
<nombre>Roberto</nombre>
<nombre>Juan</nombre>
Es decir, selecciona tanto el nombre del "propietario" como los nombres de los
"contactos"; no importa de quién sea hijo el "nombre", selecciona todos los nodos "nombre".
Para hacer lo mismo, podría usarse también, aunque la expresión queda más larga:
XPath: /agenda//nombre o /agenda/descendant::nombre
Con esa expresión se seleccionan todos los nodos nombre de la agenda, por tanto
también quedan seleccionados los 4 nombres anteriores (propietario y 3 contactos).
Otra expresión puede ser:
XPath: /agenda/contactos//nombre
Esta sentencia selecciona:
o /agenda/contactos/descendant::nombre
<nombre>Maribel</nombre>
<nombre>Roberto</nombre>
<nombre>Juan</nombre>
En este caso, a partir del nodo "contactos" selecciona todas las etiquetas "nombre",
independientemente de dónde se encuentren; aunque en este caso las tres "nombre" se
encuentran en la misma posición (dentro de "identificadores"). Si en lugar de las dos barras //
ponemos sólo una en la expresión anterior, quedaría:
XPath: /agenda/contactos/nombre
Esta sentencia no selecciona nada (conjunto vacío), porque el nodo nombre no es hijo
de contactos. Si se usa sólo una barra, hay que poner la ruta completa, es decir:
XPath: /agenda/contactos/persona/identificadores/nombre
Con los atributos también puede utilizarse la doble barra. Por ejemplo:
XPath: //@id
Esta expresión selecciona:
id="p01"
id="p02"
id="p03"
En el caso de los atributos no se pueden convertir las dos barras // en /descendant::
porque un atributo no se considera hijo del elemento al que pertenece o el elemento no se
considera padre del atributo.
Es decir, quedan seleccionados todos los atributos "id" independientemente de donde
se encuentren en el documento, aunque en este caso todos se encuentran dentro de
"persona". También podría usarse para lo mismo, aunque queda una expresión más larga:
XPath: /agenda/contactos//@id
Esta expresión selecciona los atributos id (y sus valores) que estén dentro de los
contactos de la agenda, independientemente de la posición de esos atributos (siempre que
estén dentro de contactos).
- El punto . selecciona el nodo actual.
/agenda/contactos/.
Selecciona "contactos" y todo su contenido. Obviamente selecciona lo mismo que
/agenda/contactos sin la barra final y el punto.
- Dos puntos seguidos .. selecciona el nodo padre.
/agenda/contactos/..
Selecciona "agenda" y todo su contenido, ya que desde "contactos" se va al nodo
padre (por los dos puntos .. ), y el nodo padre de "contactos" es "agenda".
El símbolo .. es equivalente a parent:: por lo que la expresión anterior podría ser
/agenda/contactos/parent:: pero esta expresión da error porque después de parent:: siempre
hay que poner algún elemento, por lo que realmente se escribiría en este caso
/agenda/contactos/parent::* para que seleccione lo mismo que /agenda/contactos/.. aunque
obviamente es más sencillo y rápido usar .. en cualquier sentencia XPath.
2.3. Predicados.
Los predicados se utilizan para encontrar nodos específicos o que contienen un valor
específico. Se escriben entre corchetes [ ] y aplican filtros a las selecciones, para quedarnos
con un conjunto de nodos más restringido. Veamos algunos ejemplos:
/agenda/contactos/persona[1]
Selecciona el primer nodo "persona" (y todo su contenido) dentro de "contactos".
/agenda/contactos/persona[3]
Selecciona el tercer nodo "persona" (y todo su contenido) dentro de "contactos".
//telefonos[2]
Selecciona el segundo nodo "telefonos" (y su contenido) de todo el documento,
independientemente de donde esté ese nodo.
//telefonos[2]/movil
Selecciona el movil del segundo nodo "telefonos".
/agenda/contactos/persona[2]/direccion/calle
Selecciona la calle (que está dentro de direccion) de la segunda persona de la agenda.
/agenda/contactos/persona[@id = "p02"]
Selecciona el nodo "persona" (y todo su contenido) cuyo atributo "id" valga "p02",
dentro de "contactos". Lógicamente, si hay varias personas cuyo atributo "id" vale "p02", serán
seleccionadas todas esas personas.
//persona[@id = "p02"]
Selecciona el nodo "persona" cuyo atributo "id" valga "p02", independientemente de
donde se encuentre el nodo "persona".
/agenda/contactos/persona[@id]
Selecciona los nodos "persona" (dentro de "contactos") que tengan atributo "id"
(independientemente del valor del "id"). No se muestran los "id", sino los nodos "persona"
completos. Como vemos, si entre corchetes sólo se pone el atributo, sin compararlo con
ningún valor concreto, se refiere a que exista el atributo.
/agenda/contactos/persona/telefonos[casa]
Selecciona los nodos "telefonos" (y su contenido) que tengan nodo "casa". No se
muestran los nodos "casa", sino los nodos "telefonos" completos. Como vemos, si entre
corchetes sólo se pone el elemento, sin compararlo con ningún valor concreto, se refiere a que
exista el elemento. Si lo que se quiere seleccionar son las personas (no sólo los nodos
"telefonos"), sería como sigue.
/agenda/contactos/persona/telefonos[casa]/..
Vemos que primero se aplica la restricción de que tengan nodo "casa" y después con
los dos puntos .. nos situamos en el nodo padre de "telefonos", que es el nodo "persona"; de
ese modo se seleccionan las personas (no sólo los teléfonos) que tengan nodo "casa".
//telefonos[casa]/../@id
Selecciona los identificadores de los que tienen telefono en casa. Hay que poner ..
(nodo padre) porque para acceder al "id" debemos subir un nivel (en el árbol de nodos) desde
el nodo "telefonos", ya que "id" pertenece al nodo "persona", y éste es el nodo padre del nodo
"telefonos". Ese predicado también podría hacerse con //casa/../../@id que no es un
predicado (no hay corchetes), simplemente es una expresión XPath; como vemos, se suben
dos niveles en el árbol de nodos (dos veces el símbolo .. ), ya que "id" pertenece al nodo
"persona" y éste está dos niveles por encima del nodo "casa". Si ponemos sólo //casa se
seleccionan los nodos "casa", pero al completarla con //casa/../../@id se seleccionan los "id"
que corresponden a esos nodos "casa".
/agenda/contactos/persona/direccion[cp]/../telefonos/movil
Selecciona el movil de las personas cuya direccion tenga cp.
/agenda/contactos/persona/direccion[localidad = "Badajoz"]
Selecciona las direcciones que tengan su localidad en Badajoz. Si queremos seleccionar
las personas (no las direcciones) de Badajoz, sería como sigue.
/agenda/contactos/persona/direccion[localidad = "Badajoz"]/..
Al usar los dos puntos .. nos situamos en el nodo padre de "direccion", es decir en
"persona"; así se seleccionan los nodos "persona" de los que cumplan la condición indicada.
/agenda/contactos/persona/direccion[localidad = "Badajoz"]/../identificadores/nombre
Selecciona el nombre (que está dentro de identificadores) de las personas cuya
localidad sea "Badajoz".
/agenda/contactos/persona/direccion[cp > "06100"]
Selecciona las direcciones cuyo cp sea mayor que 06100. También se puede poner sin
comillas (considera cp como numérico, en lugar de cadena), es decir:
/agenda/contactos/persona/direccion[cp > 06100]
//direccion[cp > 06002]/cp
Selecciona los cp de las direcciones cuyo cp sea mayor que 06002.
En XPath un dato se considera numérico cuando está formado sólo por dígitos, el
punto decimal (no la coma) y el signo - (para los negativos); si tiene alguna letra u otros
caracteres, el dato se considera cadena.
Los operadores que se puede utilizar con datos numéricos son:
=
!=
<
>
<=
>=
igual a
distinto de
menor que
mayor que
menor o igual que
mayor o igual que
Los operadores que se puede utilizar con datos no numéricos (cadenas de caracteres)
son:
=
!=
igual a
distinto de
/agenda/contactos/persona/direccion[cp >= "06"]
Selecciona las direcciones cuyo cp sea mayor o igual que "06".
//persona/direccion[cp >= 06004]/localidad
Selecciona la localidad de las direcciones cuyo cp sea mayor o igual que 06004.
/agenda/contactos/persona/direccion[localidad != "Badajoz"]/calle
Selecciona la calle de las direcciones cuya localidad no sea "Badajoz".
//persona[@id != "p01"]/telefonos
Selecciona los teléfonos de las personas cuyo id no sea "p01". El atributo "id" no es
numérico (contiene letras).
2.4. Comodines.
Existen algunos caracteres, llamados comodines, con un uso especial:
- El asterisco * significa "cualquier nodo".
/agenda
Selecciona el nodo agenda y todo su contenido; la etiqueta "agenda" también queda
incluida en la selección.
/agenda/*
Selecciona los nodos hijos (cualquiera que sea) de la agenda; la etiqueta "agenda" no
queda incluida en la selección.
//*
Selecciona todos los nodos del documento, estén donde estén.
//contactos/*
Selecciona los hijos de "contactos" de todo el documento. La etiqueta "contactos" no
queda seleccionada, sólo sus nodos hijos.
//persona[@id = "p02"]
Selecciona todos los nodos de la persona contacto cuyo atributo id valga "p02"; el
nodo persona también queda seleccionado. Pero en el caso siguiente:
//persona[@id="p02"]/*
Selecciona los nodos de la persona contacto cuyo atributo id valga "p02", pero el nodo
persona no queda seleccionado (sus nodos hijos sí, es decir quedan seleccionados los nodos
identificadores, direccion y telefonos).
//persona[@id="p02"]/*/*
Selecciona los nodos de la persona cuyo atributo id valga "p02", pero el nodo persona
no queda seleccionado, ni tampoco sus nodos hijos (sus nodos nietos sí, es decir los nodos
nombre, apellidos, calle, localidad, cp, movil, casa).
//movil/*
No selecciona nada, ya que dentro del nodo "movil" no hay otros nodos, sólo datos.
- La arroba con asterisco @* significa "cualquier atributo".
//persona[@*]
Selecciona las personas que tengan un atributo cualquiera.
//persona[@* = "p02"]
Selecciona los nodos "persona" que tengan algún atributo (uno cualquiera) con valor
"p02"
/agenda/contactos/persona/@*
Selecciona todos los atributos de las personas de la agenda.
/agenda/contactos/persona[@* = "p01"]/@*
Selecciona todos los atributos de las personas de la agenda que tengan algún atributo
con valor "p01".
2.5. Funciones.
Algunas funciones incorporadas en el lenguaje XPath son:
- boolean(): aplicada a un conjunto de nodos (a una expresión XPath) devuelve valor verdadero
(un 1) si hay nodos en el conjunto y valor falso (un 0) si no hay nodos en el conjunto. Dicho de
otro modo, si la expresión XPath selecciona algo (muestra algo), el boolean de esa expresión
dará 1; si no selecciona nada dará 0. Por tanto, la función boolean devuelve un valor booleano
(verdadero o falso, es decir 0 o 1).
boolean(/agenda/contactos)
Devuelve el valor 1 (verdadero), ya que "contactos" de agenda tiene nodos. La
expresión /agenda/contactos selecciona algo.
boolean(/agenda/persona)
Devuelve el valor 0 (falso), ya que dentro de agenda no está el nodo "persona"; al ser
conjunto vacío boolean dará falso. Si ejecutamos la expresión /agenda/persona da el mensaje
(en XML Copy Editor) "Ningún nodo correspondiente encontrado", es decir que no
seleccionada nada.
Para saber si existe o no en la agenda alguna direccion con localidad "Zafra", podemos
utilizar:
boolean(//direccion[localidad="Zafra"])
Si hay alguna direccion de Zafra devuelve valor 1; en caso contrario el 0.
- not(): cambia el valor booleano que se le indique dentro de los paréntesis; el verdadero lo
cambia a falso; el falso lo cambia a verdadero; por tanto, devuelve un valor booleano.
not ( boolean(//direccion[localidad="Zafra"]) )
Devuelve valor 0 (falso) si hay alguna dirección de Zafra, y el valor 1 (verdadero) en
caso contrario.
La función not() también se puede usar del modo siguiente.
//telefonos[not(trabajo)]
Selecciona los nodos "telefonos" que no tengan entre sus hijos el nodo "trabajo". Sería
lo contrario de //telefonos[trabajo].
//telefonos[not(trabajo)]/..
Selecciona las personas que no tengan entre sus telefonos el del trabajo. No selecciona
sólo los telefonos, sino las personas, porque se sube al nodo padre al poner los dos puntos ..
//direccion[not(localidad = "Zafra")]
Selecciona los nodos direccion cuya localidad no sea "Zafra"; por tanto, esa expresión
coincide con: //direccion[localidad != "Zafra"]
- true(): devuelve el valor booleano verdadero (valor 1).
- false(): devuelve el valor booleano falso (valor 0).
- count(): cuenta el número de nodos; esta función devuelve un valor numérico.
count(/agenda)
Devuelve el valor 1 porque nodo "agenda" sólo hay uno en el documento XML.
count(/agenda/contactos)
Devuelve el valor 1 porque sólo hay un nodo "contactos" en la agenda.
count(/agenda/contactos/persona)
Devuelve el valor 3 porque dentro de contactos hay tres nodos "persona"; la agenda
tiene 3 personas. Es equivalente a count(//persona).
count(//nombre)
Devuelve el valor 4 porque hay cuatro nodos "nombre" en toda la agenda; un nombre
del propietario y otros tres nombres de las personas de la agenda.
count(//direccion[localidad="Badajoz"])
Devuelve el valor 2 porque en la agenda hay dos direcciones cuya localidad es
"Badajoz".
- name(): devuelve el nombre de un nodo, que puede ser el nombre del primer nodo de un
conjunto; dentro de los paréntesis se pone una expresión XPath, la cual dará un resultado; el
nombre del primer nodo de este resultado es lo que muestra la función name.
name(/agenda/contactos/*)
Devuelve el nombre "persona" porque dentro de contactos el primer nodo es
"persona". La expresión /agenda/contactos/* selecciona una serie de nodos, de los cuales el
primero es "persona".
name(/agenda/contactos)
Devuelve el nombre "contactos" porque la expresión /agenda/contactos selecciona
una serie de nodos, de los cuales el primero es "contactos".
name(//persona/@id)
Devuelve "id", ya que la expresión //persona/@id muestra los atributos "id" de las
personas, por lo que el primer nodo es "id" (son nodos atributo en este caso).
- position(): devuelve la posición del nodo por el que se va pasando en cada momento (nodo
contexto). La posición se refiere al puesto que ocupa en la relación de nodos del mismo tipo.
La expresión XPath selecciona una serie de nodos, sobre la cual podemos aplicar position()
para elegir sólo algunos nodos en función de su posición.
//calle[position()>=2]
Selecciona las calles de la agenda que tengan posición 2 o mayor. La expresión //calle
selecciona todas las calles, pero al aplicar position()>=2, nos quedamos sólo con las calles a
partir del puesto 2.
//localidad[position()<3]
Selecciona las localidades con posición menor de tres.
/agenda/contactos/persona[position()>1]/direccion/calle
Selecciona la calle de las personas de la agenda cuya posición (de la persona) sea
mayor de 1 (es decir, desde la segunda persona en adelante).
- last(): devuelve la última posición de la lista de nodos; si la lista de nodos tiene 3 personas,
last() devuelve el valor 3.
//persona[ last() ]
Selecciona la última persona de la agenda.
//persona[ last() ]/*
Selecciona los nodos hijos de la última persona de la agenda; la etiqueta "persona" no
queda incluida en la selección.
/agenda/contactos/persona[ last()-1 ]/telefonos
Selecciona los telefonos de la penúltima persona de la agenda.
- normalize-space(): quita los espacios en blanco sobrantes, es decir, los que estén al inicio y al
final; además, varios espacios seguidos en medio de un valor los deja en uno. Por ejemplo, si
representamos el espacio con el carácter ?, el valor "??p???01?" quedaría como "p?01". Si en
el documento XML se escriben espacios sobrantes, por error o por otro motivo, en la expresión
XPath pueden quitarse antes de realizar una comparación con un valor, para que dicha
comparación sea más fiable.
//persona[normalize-space(@id)="p01"]
Selecciona las personas cuyo atributo "id" sea p01 después de haberle quitado los
espacios sobrantes. Si en el documento XML se ha puesto un "id" con el valor " p01 " esos
espacios son eliminados antes de evaluar la expresión XPath y la comparación con "p01" dará
valor verdadero.
//direccion[normalize-space(cp)="06002"]/..
Selecciona las personas cuyo cp sea 06002 después de quitarle los espacios sobrantes.
Al poner .. sube desde el nodo direccion al padre, que es persona, por eso selecciona la
persona completa, con todo su contenido.
- string(): convierte a cadena; del conjunto de nodos que devuelve una expresión XPath, la
función string selecciona el valor del primer nodo y lo convierte a cadena. No selecciona las
etiquetas de los nodos, sino sólo el valor.
string(//calle)
Como //calle selecciona el conjunto de nodos calles, al aplicarle string selecciona el
valor, convertido a cadena, de la primera calle; no selecciona las etiquetas <calle> y </calle>,
sino sólo el valor de la primera calle.
string(//direccion[localidad="Zafra"]/cp)
Devuelve el cp convertido a cadena de la direccion cuya localidad sea Zafra.
string(boolean(/agenda/persona))
Devuelve la cadena "false", ya que boolean(/agenda/persona) devuelve valor 0 (falso),
porque persona no es hijo de agenda; el 0 convertido a cadena corresponde con "false".
string(boolean(/agenda/contactos))
Devuelve la cadena "true", ya que boolean(/agenda/contactos) devuelve valor 1
(verdadero), porque agenda tiene nodos hijos llamados contactos; el 1 convertido a cadena
corresponde con "true".
- string-length(): devuelve la longitud de una cadena (número de caracteres).
string-length(/agenda/propietario/apellidos)
Devuelve 11, ya que los apellidos del propietario de la agenda tienen 11 caracteres, ya
que es "Pinto Ramos", es decir 10 letras más el espacio en blanco suman 11.
- concat(): concatena o une varias cadenas; estas cadenas se escriben dentro de los paréntesis
separadas por comas, o sea concat(cad1, cad2, cad3, ...) .
//identificadores[concat(nombre, apellidos) = "RobertoRamos Juan"]
Selecciona el nodo identificadores de la persona cuyo nombre concatenado con los
apellidos sea igual a "RobertoRamos Juan". Con la funcion concat las cadenas quedan pegadas
(no inserta espacios en blanco entre una y otra); en este caso el nombre queda pegado a los
apellidos. Para separar el nombre de los apellidos se hace como en el siguiente ejemplo.
//identificadores[concat(nombre, " ", apellidos) = "Roberto Ramos Juan"]
Selecciona el nodo identificadores de la persona cuyo nombre más un espacio en
blanco más los apellidos sea igual a "Roberto Ramos Juan". Como vemos, se pueden
concatenar 3 cadenas.
- sum(): suma valores numéricos.
sum(//cp)
Devuelve la suma de todos los cp.
sum(//direccion[cp=06002]/cp)
Devuelve la suma de los cp de las direcciones cuyo cp sea 06002; como en nuestro XML
sólo hay uno, muestra el cp, sólo el dato, sin las etiquetas <cp> y </cp>.
- document(): esta función realmente no es de XPath, sino de XSLT. Puede utilizarse para
acceder a elementos de otro documento XML. Entre paréntesis se puede indicar la ruta o URI
del otro documento XML, como document('prueba.xml'), y nos devuelve el elemento raíz de
ese documento. Si entre los paréntesis se indica un nodo, la función document() nos devuelve
el conjunto de nodos cuyo elemento raíz es el nodo dado.
2.6. Varias selecciones.
Con el operador | se pueden realizar varias selecciones en la misma expresión XPath.
//direccion | //telefonos
Selecciona todas las direcciones y todos los telefonos
//direccion[localidad="Badajoz"]/calle | /agenda/contactos/persona/identificadores/nombre
Selecciona la calle de los que sean de Badajoz y los nombres de todos los contactos.
//direccion[localidad="Badajoz"]/calle | //direccion[localidad="Zafra"]/calle
Selecciona la calle de los que sean de Badajoz y de los que sean de Zafra.
//persona/identificadores/nombre | //persona/identificadores/apellidos
Selecciona nombre y apellidos de todas las personas de la agenda.
//contactos/persona/identificadores/nombre | //contactos/persona/@id
Selecciona nombre e identificador de cada contacto de la agenda.
2.7. Predicados compuestos.
Con los operadores "and" y "or" se pueden crear condiciones compuestas.
//direccion[localidad = "Badajoz" or localidad = "Zafra"]
Selecciona las direcciones que sean de "Badajoz" o de "Zafra". Lo que hace es que para
cada localidad se comprueba si coincide con "Badajoz" o con "Zafra" y selecciona las
direcciones en las que esa comprobación resulte verdadera (true o 1). Para que resulte
verdadera una expresión que tiene or, debe ser verdadera una de las dos comparaciones que
se incluyen, o las dos. Podrían ser tres comparaciones o más. En cuanto se cumpla una de las
comparaciones, el resultado del or será verdadero.
//direccion[localidad = "Badajoz" and localidad = "Zafra"]
No selecciona ningún elemento, ya que ningún elemento tiene localidad igual a
Badajoz y a la vez igual a Zafra. Lo que hace es que para cada localidad se comprueba si
coincide con "Badajoz" y a la vez con "Zafra" y selecciona las direcciones en las que esa
comprobación resulte verdadera (true o 1), pero lógicamente ninguna localidad vale a la vez
"Badajoz" y "Zafra". Todas las comprobaciones darán falso (false o 0). Para que resulte
verdadera una expresión que tiene and, deben ser verdaderas las dos comparaciones que se
incluyen. Podrían ser tres comparaciones o más. En cuanto no se cumpla una de las
comparaciones, el resultado del and será falso.
//direccion[localidad = "Badajoz" and cp > 06002]
Selecciona las direcciones de Badajoz y con cp mayor que 06002, que cumplan las dos
condiciones (and). Si se cumplen las dos comparaciones, el resultado del and será verdadero.
//direccion[localidad = "Badajoz" or cp > "06002"]
Selecciona las direcciones de Badajoz o con cp mayor que "06002"; es decir las
direcciones que cumplan una de las dos condiciones o ambas (or). Si se cumple una (o las dos)
de las dos comparaciones, el resultado del or es verdadero.
3. XSLT.
3.1. Introducción.
El lenguaje de marcas XSLT (XSL Transformation o Transformación XSL) permite
transformar un documento XML, por ejemplo para mostrarlo como página web. XSL significa
eXtensible Stylesheet Language (Lenguaje Extensible de Hojas de Estilo) y es un lenguaje que
cumple el estándar XML. XSL tiene varias partes; una de ellas es la dedicada a XSLT, es decir a
la transformación o conversión de un documento XML, por ejemplo en otro XML o en un
HTML.
Tanto XSL como XSLT son lenguajes XML.
Un documento escrito con el lenguaje XSLT (una hoja XSLT) también es un documento
XML, ya que cumple las especificaciones XML de W3C.
XSLT también es un estándar aprobado por el W3C, con el que podemos transformar o
convertir un documento XML en:


Otro documento XML.
Un documento HTML.

Un documento de texto.
3.2. Estructura de una hoja XSLT.
Para escribir las hojas o documentos XSLT usaremos el programa "XML Copy Editor".
Cuando un documento XML está asociado a una hoja o documento XSLT, debe
indicarse, después del prólogo del documento XML, con la línea:
<?xml-stylesheet type="text/xsl" href="fichero.xsl"?>
Donde fichero.xsl será la hoja o documento XSLT asociado al XML. En ese atributo
href="..." puede indicarse la ruta completa del fichero, por ejemplo:
<?xml-stylesheet type="text/xsl" href="C:\carpeta\prueba\fichero.xsl"?>
<?xml-stylesheet type="text/xsl" href="http://www.pagina.es/directorio/fichero.xsl"?>
Veámoslo con un ejemplo concreto. Teniendo el documento XML utilizado para las
expresiones XPath anteriores (la agenda), se le añade la línea anterior para indicar que lleva
asociado un fichero XSLT, del modo:
fichero .xml
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<!-- La siguiente línea hace referencia al documento XSLT que hará la transformación -->
<?xml-stylesheet type="text/xsl" href="transformacion01.xsl"?>
<agenda>
<propietario>
<nombre>Ana</nombre>
<apellidos>Pinto Ramos</apellidos>
</propietario>
<contactos>
<persona id="p01">
etc etc etc
.......
</agenda>
Como vemos, la línea ?xml-stylesheet se utiliza para especificar el fichero .xsl que
contiene la transformación que se va a aplicar al .xml; en este caso ese fichero se llama
transformacion01.xsl, el cual debe guardarse en la misma carpeta que el fichero.xml en este
ejemplo, porque en el href no se especifica una ruta distinta.
Por otra parte, la estructura de un documento XSLT comienza con las dos líneas
siguientes:
fichero .xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
En la primera línea se indica que también se trata de un documento XML.
Un ejemplo completo del fichero anterior, "transformacion01.xsl", podría ser el
siguiente:
transformación01.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Primera transformación</title></head>
<body bgcolor="#00dd00">
<h1>Agenda</h1>
<h2>Propietario</h2>
<b>Apellidos y nombre: </b>
<xsl:value-of select="agenda/propietario/apellidos"/>,
<xsl:value-of select="agenda/propietario/nombre"/>
<br/>
<h2>Contactos</h2>
<b>Apellidos y nombre: </b>
<xsl:value-of select="agenda/contactos/persona[1]/identificadores/apellidos"/>,
<xsl:value-of select="agenda/contactos/persona[1]/identificadores/nombre"/>
<b> - identificador: </b>
<xsl:value-of select="agenda/contactos/persona[1]/@id"/>
<br/>
<b>Apellidos y nombre: </b>
<xsl:value-of select="agenda/contactos/persona[2]/identificadores/apellidos"/>,
<xsl:value-of select="agenda/contactos/persona[2]/identificadores/nombre"/>
<b> - identificador: </b>
<xsl:value-of select="agenda/contactos/persona[2]/@id"/>
<br/>
<b>Apellidos y nombre: </b>
<xsl:value-of select="agenda/contactos/persona[3]/identificadores/apellidos"/>,
<xsl:value-of select="agenda/contactos/persona[3]/identificadores/nombre"/>
<b> - identificador: </b>
<xsl:value-of select="agenda/contactos/persona[3]/@id"/>
<br/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Una vez que hemos escrito con el programa "XML Copy Editor" ambos documentos, el
XML y el XSLT, abrimos el fichero .xml con dicho programa. Puede abrirse a la vez también el
fichero .xsl, pero no es obligatorio. Si tenemos ambos abiertos en "XML Copy Editor", nos
situamos en el fichero .xml y en la opción "XML - XSL Transformación" del menú superior (o
pulsando la tecla F8) aplicamos la transformación y se mostrará el código resultante, que en
este caso es el código HTML siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>Primera transformación</title>
</head>
<body bgcolor="#00dd00"><h1>Agenda</h1><h2>Propietario</h2><b>Apellidos y nombre:
</b>Pinto Ramos,
Ana<br/><h2>Contactos</h2><b>Apellidos y nombre: </b>Lago Santana,
Maribel<b> - identificador: </b>p01<br/><b>Apellidos y nombre: </b>Ramos Juan,
Roberto<b> - identificador: </b>p02<br/><b>Apellidos y nombre: </b>Lima Ruz,
Juan<b> - identificador: </b>p03<br/></body>
</html>
Si guardamos ese código HTML en un fichero con extensión .html y lo abrimos con un
navegador web podremos ver el resultado.
Un modo más sencillo y rápido de mostrar esa transformación en un navegador web es
el siguiente. Se trata simplemente de abrir el fichero .xml con el navegador web. Para que
funcione en Chrome debe ejecutarse con la opción --allow-file-access-from-files, para lo cual
puede crearse un nuevo lanzador o acceso directo con la "Ubicación" o "Destino" siguiente:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files
El navegador web aplicará la transformación xsl asociada sobre el fichero xml y
mostrará algo como:
En el .xsl se indica que el fondo de página sea verde #00dd00, algunas palabras estén
en negrita <b>, algunas palabras más grandes <h1>, etc.
Si en el navegador web usamos la opción de "Ver código fuente" se muestra el
documento en formato XML, pero si usamos la opción "Archivo - Guardar como" podremos
guardar el resultado en formato HTML, obteniendo un documento con formato HTML creado
por medio de la transformación .xsl que se ha aplicado.
Como vemos, en este caso el .xsl transforma el fichero .xml en un documento HTML,
que podrá mostrarse en el navegador web. Este fichero .xsl contiene etiquetas HTML, como
<body>, <h1>, etc. (cumplen la sintaxis XML) y otras etiquetas <xsl: .../>; en este caso las
<xsl:value-of select="..."/> son para seleccionar los datos del .xml que se van a mostrar, por
ejemplo:
<xsl:value-of select="agenda/propietario/apellidos"/>
Dentro del select (entre las comillas) se ponen expresiones XPath que seleccionan los
datos. Por tanto, el elemento value-of resuelve la expresión XPath que se indique dentro del
select, es decir permite calcular una expresión XPath. Vemos que la etiqueta <xsl: ... /> tiene al
final la barra / de cierre de etiqueta.
La expresión XPath incluye los nombres de las etiquetas o nodos, pero al aplicar valueof select se queda con el dato, quitando las etiquetas, es decir:
XPath: /agenda/propietario/apellidos
selecciona <apellidos>Pinto Ramos</apellidos>
XSLT: <xsl:value-of select="agenda/propietario/apellidos"/> selecciona Pinto Ramos
Como vemos, el .xsl es un documento con formato XML, por lo que su prólogo es:
<?xml version="1.0" encoding="UTF-8"?>
La línea siguiente indica la versión del XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
Esta etiqueta debe cerrarse al final del fichero .xsl, del modo:
</xsl:stylesheet>
La línea siguiente es:
<xsl:template match="/">
La función xsl:template (plantilla) se cierra con la línea </xsl:template>. En nuestro
caso se cierra al final, antes de cerrar el documento con </xsl:stylesheet>), por lo que
prácticamente todo queda dentro de la función xsl:template, la cual permite definir el formato
a aplicar sobre los datos XML.
Aparte de la función xsl:template, existe la función xsl:apply-templates, que se escribe
dentro de las etiquetas de apertura y cierre de xsl:template y sirve para aplicar una plantilla
definida sobre unos datos XML. En nuestro ejemplo no la utilizamos, no es necesaria.
En nuestro ejemplo, la línea <xsl:template match="/"> indica que todas las
expresiones XPath que contenga el fichero .xsl parten desde el nodo raíz, es decir: match="/".
En lugar de ese match se podría haber usado el siguiente:
<xsl:template match="/agenda">
En tal caso, al escribir las expresiones XPath del fichero .xsl nos ahorramos repetir
"agenda/" en todos los "select", quedando por ejemplo:
<xsl:value-of select="propietario/apellidos"/>,
<xsl:value-of select="propietario/nombre"/>
...
<xsl:value-of select="contactos/persona[1]/identificadores/apellidos"/>,
<xsl:value-of select="contactos/persona[1]/identificadores/nombre"/>
Como vemos, las rutas de estos select no comienzan por / sino directamente por el
nombre del elemento en cuestión, es decir:
propietario/apellidos
propietario/nombre
contactos/persona[1]/identificadores/apellidos
contactos/persona[1]/identificadores/nombre
Si se pone la barra inicial, por ejemplo las rutas /propietario/apellidos o
/propietario/nombre, la transformación no funcionará, porque busca en el fichero .xml un
nodo llamado propietario que cuelgue directamente del nodo raiz, y ese nodo no existe.
En un select se puede poner una ruta que no comience por la barra / y en tal caso se
trata de una ruta relativa, a la que debe añadirse la ruta indicada en el match para acceder al
nodo en cuestión. Pero en el select también se puede poner una ruta comenzando por la barra
/ siendo una ruta absoluta, y en tal caso no se tiene en cuenta la ruta indicada en el match.
Por ejemplo, si tenemos el match <xsl:template match="/agenda"> los siguientes select
serán:
<xsl:value-of select="propietario/apellidos"/>
A esta ruta relativa (no comienza por / barra) se le añade la ruta indicada en match,
quedando /agenda/propietario/apellidos. Selecciona los apellidos del propietario.
<xsl:value-of select="/agenda/propietario/apellidos"/>
Esta ruta es absoluta (comienza por / barra); no se le añade nada; no se tiene en
cuenta la indicada en match. Selecciona los apellidos del propietario.
<xsl:value-of select="/propietario/apellidos"/>
Esta ruta es absoluta; no se le añade nada; no se tiene en cuenta la ruta del match;
pero no selecciona nada, porque el nodo propietario no cuelga del nodo raíz en el xml.
Otro elemento XSLT es xsl:output, que define el formato del documento de salida, que
puede ser xml, html o text, lo cual se indica en el atributo method de dicho elemento. Este
elemento no es obligatorio usarlo, porque generalmente el formato de salida se detecta
automáticamente. Si se utiliza, se escribe antes de xsl:template y se cierra en la misma línea
poniendo la barra / al final, por ejemplo:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html"/>
<xsl:template match="/">
etc etc etc
Otro elemento XSLT es xsl:preserve-space que marca los elementos que no tienen
espacios en blanco antes de la transformación; dicho de otro modo, indica los elementos a los
que no se les va a quitar los espacios, éstos serán respetados, que es la opción por defecto. Por
el contrario, el elemento xsl:strip-space marca los elementos que tienen espacios eliminados
antes de la transformación; es decir, indica los elementos a los que se les va a quitar los
espacios. Estos elementos se escriben antes de xsl:template y los nombres de los elementos se
indican separados por espacios, aunque se puede poner * para indicar todos los elementos.
Por ejemplo:
fichero01.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="transformacion01.xsl"?>
<persona id="p01">
<identificadores>
<nombre>Maribel</nombre> <apellidos>Lago Santana</apellidos>
</identificadores>
<direccion>
<calle>Mayor 22, 2B</calle>
<localidad>Badajoz</localidad>
<cp>06002</cp>
</direccion>
<telefonos>
<movil>666111111</movil>
</telefonos>
</persona>
transformacion01.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:preserve-space elements="*"/>
<xsl:template match="/">
<xsl:value-of select=" persona"/>
</xsl:template>
</xsl:stylesheet>
Si con "XML Copy Editor" aplicamos ese xsl al documento xml, con la opción "XML - XSL
Transformación" (o tecla F8), tendremos el resultado:
<?xml version="1.0" encoding="UTF-8"?>
Maribel
Lago Santana
Mayor 22, 2B
Badajoz
06002
666111111
Si en el xsl cambiamos preserve-space por strip-space tendremos el resultado:
<?xml version="1.0" encoding="UTF-8"?>
MaribelLago SantanaMayor 22, 2BBadajoz06002666111111
Vemos que en este último caso no se respetan los espacios ocupados por las etiquetas
del xml (todas las etiquetas, porque hemos usado asterisco *), quedando todos los datos
seguidos.
Otro elemento es xsl:decimal-format que convierte datos numéricos en cadenas;
realmente define un formato que se aplicará cuando se conviertan números en cadenas.
3.3. Estructura repetitiva (for-each).
Una de las órdenes más utilizadas en xsl es "for-each" (bucle) que permite recorrer una
serie de nodos con el mismo nombre. En el ejemplo anterior hemos recorrido las tres personas
de la agenda usando los predicados [1] [2] y [3] (persona[1] persona[2] y persona[3]). Si en
lugar de tres personas, la agenda tuviera muchas más (por ejemplo 200 personas), el fichero
.xsl escrito de ese modo sería muy extenso (desde persona[1] hasta persona[200). La forma
más correcta de resolverlo es con la orden "for-each" que significa "para-cada"; en el caso de
las personas de la agenda, será "para-cada persona". Quedaría como sigue:
fichero .xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Segunda transformación</title></head>
<body bgcolor="#0000dd">
<h1>Agenda</h1>
<h2>Contactos - <xsl:value-of select="count(/agenda/contactos/persona)"/></h2>
<xsl:for-each select="/agenda/contactos/persona">
<b>Apellidos y nombre: </b>
<xsl:value-of select="identificadores/apellidos"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="identificadores/nombre"/>
<br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Como vemos, el formato para procesar cada persona es:
<xsl:for-each select="/agenda/contactos/persona">
Como hemos dicho antes, podría evitarse la / barra inicial y usar la ruta relativa a la
indicada en match, quedando:
<xsl:for-each select="agenda/contactos/persona">
En "select" se indica el nodo que se desea recorrer todas las veces que aparezca en el
.xml (en este caso es /agenda/contactos/persona). Si la agenda tuviera 200 personas, ese
fichero .xsl no sería más extenso, con for-each se recorren las 200 personas con la misma
orden.
Lógicamente, la orden for-each debe tener su etiqueta de cierre, que es:
</xsl:for-each>
Como vemos en el ejemplo, dentro de la etiqueta for-each, las expresiones XPath no
repiten la ruta completa del nodo; es decir, si el select de for-each ya nos ha situado en el
nodo "/agenda/contactos/persona", los select de value-of dentro del for-each no repiten esa
ruta, sino que la continúan, por ello para mostrar los apellidos y nombre sólo se pone
"identificadores/apellidos" y "identificadores/nombre" (rutas relativas). Ahí deben ponerse
obligatoriamente las rutas relativas, para que el for-each vaya avanzando de persona.
En ese ejemplo también vemos que para insertar texto se puede usar la etiqueta
<xsl:text>. En este caso se ha utilizado para insertar una coma y espacio en blanco entre los
apellidos y el nombre de cada persona:
<xsl:text>, </xsl:text>
Además hemos usado la función count() de XPath para mostrar cuántas personas tiene
la agenda, mostrando el número 3 al lado de "Contactos - ", con la instrucción:
<h2>Contactos - <xsl:value-of select="count(/agenda/contactos/persona)"/></h2>
El resultado de la transformación será:
3.4. Estructura condicional (if test).
La estructura condicional se utiliza para mostrar sólo los datos que cumplan una
condición. Por ejemplo, para mostrar sólo los contactos cuya localidad sea 'Badajoz', sería:
fichero .xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Tercera transformación</title></head>
<body bgcolor="#dd0000">
<h1>Agenda</h1>
<h2>Contactos de Badajoz - <xsl:value-of
select="count(//direccion[localidad='Badajoz'])"/></h2>
<xsl:for-each select="/agenda/contactos/persona">
<xsl:if test="direccion/localidad='Badajoz'">
<b>Apellidos y nombre: </b>
<xsl:value-of select="identificadores/apellidos"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="identificadores/nombre"/>
<br/>
</xsl:if>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Como vemos, la instrucción "if test" permite aplicar una condición; 'Badajoz' se escribe
con comillas simples porque las comillas dobles ya están usadas para el test: if test="...". Al
incluir el "if test" dentro de la estructura repetitiva (for-each), comprueba la condición para
cada persona de la agenda.
Como ya se ha indicado previamente, el for-each nos ha situado en el nodo persona,
dentro del for-each sólo se especifica la ruta partiendo de ese nodo, por eso en el "if test" sólo
se escribe "direccion/localidad", en lugar de escribir la ruta completa que sería:
"/agenda/contactos/persona/direccion/localidad".
El resultado de esa transformación es:
Podemos decir que la función xsl:if permite decidir entre dos formatos distintos en
función de una condición.
Como vemos, con la función de XPath count(//direccion[localidad='Badajoz'])
mostramos el número de contactos de Badajoz. Ese count() usa una ruta absoluta (comienza
por / barra, ya que pone //direccion.... ), pero está fuera del for-each, por tanto es correcto.
Lógicamente la condición indicada en test puede ser compuesta, utilizando los
operadores "and" y "or" de XPath, por ejemplo:
<xsl:if test="direccion/localidad='Badajoz' and telefonos/casa">
Muestra los contactos que sean de 'Badajoz' y tengan teléfono en casa.
<xsl:if test="direccion/localidad='Badajoz' or direccion/localidad='Zafra'">
Muestra los contactos que sean de 'Badajoz' o de 'Zafra'.
3.5. Estructura condición múltiple (choose when otherwise).
Cuando se desea especificar varias condiciones anidadas se utiliza "choose when
otherwise", para decidir qué formatos se aplican en función de esas condiciones. Por ejemplo:
- Si una persona de la agenda tiene teléfono trabajo, mostrar nombre con el teléfono trabajo.
- Si no tiene teléfono trabajo, si tiene teléfono de casa, mostrar nombre con tfno casa.
- Si tampoco tiene teléfono de casa, si tiene teléfono móvil, mostrar nombre con tfno móvil.
- Si tampoco tiene teléfono móvil, mostrar nombre y el texto "No tiene teléfono".
Esa situación se escribiría como sigue:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Transformación</title></head>
<body bgcolor="#00dddd">
<h1>Agenda</h1>
<h2>Contactos</h2>
<xsl:for-each select="/agenda/contactos/persona">
<xsl:choose>
<xsl:when test="telefonos/trabajo">
<b>Apellidos y nombre - tfno trabajo: </b>
<xsl:value-of select="concat(identificadores/apellidos, ', ',identificadores/nombre, ' ', telefonos/trabajo)"/>
<br/>
</xsl:when>
<xsl:when test="telefonos/casa">
<b>Apellidos y nombre - tfno casa: </b>
<xsl:value-of select="concat(identificadores/apellidos, ', ',identificadores/nombre, ' ', telefonos/casa)"/>
<br/>
</xsl:when>
<xsl:when test="telefonos/movil">
<b>Apellidos y nombre - tfno movil: </b>
<xsl:value-of select="concat(identificadores/apellidos, ', ',identificadores/nombre, ' ', telefonos/movil)"/>
<br/>
</xsl:when>
<xsl:otherwise>
<b>Apellidos y nombre - : </b>
<xsl:value-of select="concat(identificadores/apellidos, ', ',identificadores/nombre, ' ', 'No tiene teléfono')"/>
<br/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
El resultado será:
En este caso el <xsl:choose> está dentro de un for-each.
Como vemos, se pone una sóla sentencia <xsl:choose> dentro de la cual se incluyen
varias <xsl:when test...>. La forma de procesar cada persona de la agenda (for-each) es la
siguiente:
- Se comprueba la condición del primer "when test"; si la cumple se ejecutan las sentencias
que están dentro (hasta la etiqueta de cierre </xsl:when>) de ese primer when; y ya no
comprueban más condiciones (es decir, no realiza más when ni el otherwise), por lo que se
continua por la línea siguiente al cierre </xsl:choose>. Si no la cumple, se pasa al segundo
"when test".
- Si cumple la condición de ese segundo "when test", ejecuta las sentencias que tenga dentro y
ya no comprueban más condiciones (ni when ni otherwise). Si no la cumple, pasa al tercer
"when test". Y así sucesivamente.
- Si no cumple ninguna de las condiciones de los "when test", se ejecutan las sentencias
incluidas en "otherwise".
Debe tenerse en cuenta que todo lo que se incluya en xsl:choose debe estar dentro de
los xsl:when o dentro de xsl:otherwise, no puede ponerse información fuera de los mismos.
Podemos deducir de lo explicado que el xsl:choose podría sustituirse por varios xsl:if,
uno por cada xsl:when y otro para el xsl:otherwise. Quedarían en este ejemplo 4 xsl:if. Sin
embargo, para este tipo de situaciones es mejor xsl:otherwise, por ahorro de código y por
rapidez de ejecución.
Como vemos en ese ejemplo, con la función concat() de XPath se pueden concatenar
varias cadenas antes de mostrarlas.
Otro ejemplo de "when test": para cada persona de la agenda, si es de Badajoz mostrar
sus apellidos y su teléfono móvil; si es de Cáceres mostrar sus apellidos y su teléfono de casa;
si no es ni de Badajoz ni de Cáceres, mostrar apellidos y el mensaje "Esta persona no es de
Badajoz ni de Cáceres". Sería:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head>
<title>Transformación</title>
</head>
<body bgcolor="#dd00dd">
<h1>Agenda</h1>
<h2>Contactos</h2>
<xsl:for-each select="/agenda/contactos/persona">
<xsl:choose>
<xsl:when test="direccion/localidad='Badajoz'">
<xsl:value-of select="concat(identificadores/apellidos, ' - Tfno móvil: ',
telefonos/movil)"/>
<br/>
</xsl:when>
<xsl:when test="direccion/localidad='Cáceres'">
<xsl:value-of select="concat(identificadores/apellidos, ' - Tfno. casa: ',
telefonos/casa)"/>
<br/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(identificadores/apellidos, ' - Esta persona no es de Badajoz
ni de Cáceres')"/>
<br/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
El resultado es:
Otro ejemplo de "when test": mostrar el nombre y cp en tamaño <h3> de las personas
cuyo cp esté entre 06001 y 06010; del resto de personas mostrar su nombre y cp en tamaño
<h4>. Sería:
<xsl:for-each select="/agenda/contactos/persona">
<xsl:choose>
<xsl:when test="direccion/cp > '06001' and direccion/cp < '06010' ">
<h3><xsl:value-of
select="concat(identificadores/nombre,
'
direccion/cp)"/></h3>
</xsl:when>
cp:
',
<xsl:otherwise>
<h4><xsl:value-of
direccion/cp)"/></h4>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
select="concat(identificadores/nombre,
'
-
cp:
',
Como vemos, para escribir la condición de cp entre 06001 y 06010 se usa "and"
(condición compuesta):
<xsl:when test="direccion/cp > '06001' and direccion/cp < '06010' ">
Además, en lugar del signo "mayor que" > se usa la entidad predefinida > y en
lugar del signo "menor que" < se usa la entidad predefinida < para evitar errores
sintácticos.
3.6. Ordenación (sort).
La orden sort nos permite ordenar datos antes de mostrarlos, según el criterio que se
indique. La sintaxis es <xsl: sort select=" ... criterio de ordenación ... "/>
La orden sort debe escribirse justo debajo de la orden <xsl: for-each ...>.
Por ejemplo, mostrar apellidos, nombre, localidad y el id de todas las personas de la
agenda, ordenadas por apellidos.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Transformación</title></head>
<body bgcolor="#aaaa00">
<h1>Agenda</h1>
<h2>Contactos</h2>
<xsl:for-each select="/agenda/contactos/persona">
<xsl:sort select="identificadores/apellidos"/>
<xsl:value-of
select="identificadores/apellidos"/>,
<xsl:value-of
select="identificadores/nombre"/> - <xsl:value-of select="direccion/localidad"/> - <xsl:value-of
select="./@id"/>
<br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
El resultado será:
Como vemos, para acceder al "id" se pone ./@id es decir que se usa el punto para
comenzar en el nodo actual, ya que el for-each nos ha situado en /agenda/contactos/persona
y el "id" pertenece a "persona2. Desde el nodo persona para acceder al "id" la ruta es esa:
./@id
Si
en
for-each
ponemos
otro
nodo,
por
ejemplo
/agenda/contactos/persona/identificadores, el código cambiaría del modo siguiente:
<xsl:for-each select="/agenda/contactos/persona/identificadores">
<xsl:sort select="apellidos"/>
<xsl:value-of select="apellidos"/>, <xsl:value-of select="nombre"/>
- <xsl:value-of select="../direccion/localidad"/> - <xsl:value-of select="../@id"/>
<br/>
</xsl:for-each>
Es decir, si estamos situados en el nodo "identificadores", en "sort select" sólo debe
indicarse "apellidos" (que es nodo hijo de identificadores). Sin embargo, para acceder a la
localidad y el id se usan los dos puntos seguidos .. para subir al nodo padre (nos situamos en
nodo persona); el nodo padre de identificadores es persona. Por ello, si partimos de
identificadores el nodo ../direccion/localidad permite acceder a la localidad; y ../@id
permite acceder al "id".
El orden por defecto de sort es de menor a mayor (ascendente); pero se puede indicar
que sea orden de mayor a menor (descendente). Esto se elige con la opción order y los valores
"ascending" o "descending", del modo:
<xsl:sort select="apellidos" order="ascending"/>
o
<xsl:sort select="apellidos" order="descending"/>
Si no se indica order, coge el valor "ascending", es el valor por defecto.
3.7. Otro ejemplo.
Lógicamente, se puede incluir cualquier etiqueta HTML en el .xsl, para dar el formato
más conveniente. Por ejemplo, para crear una tabla a partir de la agenda se deben insertar las
etiquetas <table>, <tr> y <td> en los puntos adecuados; podría ser algo como:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head><title>Transformación</title></head>
<body bgcolor="#88aaaa">
<h1>Agenda</h1>
<h2>Contactos</h2>
<table border="3"><tr
bgcolor="#00dddd"><td>Apellidos</td><td>Nombre</td><td>Localidad</td><td>Id</td></tr>
<xsl:for-each select="/agenda/contactos/persona/identificadores">
<xsl:sort select="apellidos" order="descending"/>
<tr><td><xsl:value-of select="apellidos"/></td>
<td><xsl:value-of select="nombre"/></td>
<td><xsl:value-of select="../direccion/localidad"/></td>
<td><xsl:value-of select="../@id"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
El documento .xml con ese formato .xsl quedaría:
Como vemos el orden aplicado es de mayor a menor, porque se ha indicado
order="descending".
3.8. Procesadores y depuradores XSLT.
Un procesador XSLT es capaz de interpretar lógicamente el lenguaje XSLT y no otros
lenguajes. Se trata de un software que lee un documento XSLT y otro XML, creando un
documento de salida aplicando las instrucciones de la hoja de estilos XSLT a la información del
documento XML.
Existen procesadores XSLT que pueden ejecutarse desde la línea de comandos, como
Xalan de Apache o SAXON (Open Source).
Como hemos podido comprobar, los navegadores web llevan integrado dicho
procesador XSLT, por eso al abrir un documento XML que lleva asociado una hoja XSLT, el
navegador es capaz de aplicar la transformación y mostrar el resultado. También los servidores
web pueden llevar integrados procesadores XSLT, lo cual les permitirá alojar documentos XML
y XSLT, aplicando las transformaciones correspondientes.
Además, la mayoría de editores XML, como "XML Copy Editor", tienen la opción de
aplicar la transformación XSLT al documento XML. Ya hemos mencionado que en "XML Copy
Editor" el procesador XSLT está en la opción "XML - XSL Transformación" (o tecla F8).
También existen los llamados depuradores XSLT (debbuger). Se trata de software que
permite ver cómo se transforma el documento XML al ir ejecutando las sentencias del
documento XSLT. Es decir, con los depuradores se puede seguir la generación de un
documento, a partir de los datos del fichero .xml a los que se le aplica la hoja de estilo .xsl.
Descargar