Tema 1. XML: conceptos básicos

1.1. Estructura de un documento XML

El siguiente es un ejemplo de documento XML, que podría describir las novedades del mes en una hipotética tienda web de discos y libros:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<?xml-stylesheet type="text/css" href="novedades.css" ?>
<novedades>
   <fecha> Diciembre 2002 </fecha>
   <disco genero="pop">
      <titulo>O sea</titulo>
      <interprete>Enrique Iglesias</interprete>
      <direccion>http://www.enriqueiglesias.com</direccion>
   </disco>
   <libro>
      <titulo>Programación de aplicaciones web</titulo>
      <autor>John McCarra</autor>
      <autor>Frank Webini</autor>
      <direccion>http://www.paw.org</direccion>
   </libro>
   <libro>
      <titulo>Manolito Gafotas</titulo>
      <autor>Elvira Lindo</autor>
      <direccion>http://www.manolitogafotas.com</direccion>
   </libro>
</novedades>

Como puede observarse, el aspecto general del fichero es vagamente similar al de un texto HTML. La diferencia fundamental es que las etiquetas no son estándar, sino que han sido definidas por el usuario.

1.1.1. Partes del documento

En general, un documento XML bien formado tiene tres partes:

1.1.2 Declaración de documento XML

Se recomienda que todos los documentos XML comiencen con esta declaración, que los identifica como tales. En caso de estar presente, debe ser la primera línea del documento. Esta declaración proporciona información general: la versión de XML, el juego de caracteres y si hace referencia a entidades externas. Por ejemplo:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

El atributo version indica la versión de XML empleada (la actual y única existente hasta la fecha es la 1.0). El atributo opcional encoding especifica el juego de caracteres empleado (en este caso el europeo occidental y latinoamericano, si no se especifica por defecto se supone el inglés). Finalmente, el atributo opcional standalone indica si se hace referencia a entidades externas al documento (en ese caso, el documento no es "independiente" o standalone).

La declaración de documento XML es un caso particular de las llamadas instrucciones de procesamiento.

1.1.3. Instrucciones de procesamiento

Son las instrucciones que comienzan con los símbolos <? y terminan con ?>. Estas instrucciones van destinadas al software que analice o procese el documento XML, y por tanto son dependientes del mismo. Por ejemplo, la instrucción <?xml-stylesheet?> le indica a los procesadores que sean capaces de reconocerlo que el estilo visual asociado al documento está almacenado en una hoja de estilo determinada.

1.1.4 Etiquetas y elementos

La parte principal de un documento XML son las etiquetas de marcado (que, al estilo de HTML, van entre símbolos < y >). Un par de etiquetas de marcado, una de apertura y una de cierre, con un texto en medio, constituyen lo que se denomina un elemento, por ejemplo

<interprete> Enrique Iglesias </interprete>

En XML, cada etiqueta de apertura debe tener su correspondiente de cierre (esto no se cumple en HTML con etiquetas como <br> o <hr>). No obstante, se pueden crear etiquetas vacías, poniendo una barra al final del nombre. Por ejemplo, la etiqueta <br> de HTML, expresada en sintaxis válida de XML sería <br/>. XML diferencia entre mayúsculas y minúsculas, por lo que </INTERPRETE> no se consideraría como etiqueta de cierre válida en el ejemplo anterior.

Elemento raíz

En cada fichero XML debe haber uno y solo un elemento raíz, que contiene al resto de elementos del documento. En el documento del ejemplo, el elemento raíz sería <novedades>.

Atributos

Las etiquetas pueden contener atributos, pero a diferencia de HTML, se exige que estén entre comillas:

<disco genero="pop">

Entidades predefinidas

Algunos caracteres, como < y > tienen un significado especial en XML y por tanto no pueden emplearse en el texto que va entre las etiquetas. Para incluir estos y otros símbolos se emplean unas secuencias de escape, al estilo de las de HTML. La siguiente tabla recoge la equivalencia entre símbolos y secuencias de escape

Símbolo Secuencia
& &amp;
< &lt;
> &gt;
' (apóstrofe) &apos;
" (comillas dobles) &quot;

A estas secuencias de escape se las suele denominar entidades predefinidas. Aunque son las únicas que incorpora el estándar, el usuario puede definir sus propias entidades.

Secciones CDATA

Si en un documento se usan muchas entidades predefinidas (como puede ser el caso de un XML que contenga JavaScript, donde los operadores < y > son comunes), el texto se hace farragoso y difícil de leer. Para evitar esto, se puede incluir en una sección CDATA, que indica al procesador que esté analizando el XML que debe dejar esa sección sin analizar y tratarla como texto puro. Una sección CDATA comienza con los símbolos <![CDATA[ y termina con ]]>.

1.2. Validación de documentos XML

Cuando un documento XML tiene la estructura descrita en el apartado anterior, se dice que está bien formado. No obstante, si varias personas van a escribir documentos XML del mismo tipo sería conveniente que pudieran especificar qué etiquetas son válidas y en qué orden deben figurar en el documento. Esto sería el equivalente a definir una gramática para cierto tipo de documento XML. En la actualidad existen dos formas de definir gramáticas para XML (dos lenguajes de definición):

Un documento bien formado que además cumple la gramática definida por el usuario se dice que es un documento válido.

1.3. Espacios de nombres

Al definir cada usuario su propio conjunto de etiquetas, podría darse el caso de que dos usuarios definieran la misma etiqueta con fines diferentes. Por ejemplo, en un documento XML que almacenara un pedido, podría haber una etiqueta <direccion>, que se refiriera a la dirección de envío del pedido (y no a una URL como en el caso del ejemplo). Esto no es un problema en tanto en cuanto los documentos se mantengan separados, pero sí lo sería si se fusionan (por ejemplo, podría ser interesante enviarle al usuario una lista de discos recomendados, del mismo estilo que el que ha pedido). Para evitar problemas se introduce el concepto de espacio de nombres, que permite tener dos etiquetas distintas con el mismo nombre, siempre que se sitúen en distinto "espacio".

1.3.1 Definir un espacio de nombres

Cada espacio de nombres se define mediante un nombre corto y un nombre largo. Estos nombres se especifican en el atributo xmlns de la etiqueta raíz (normalmente). Es decir:

<etiqueta-raiz xmlns:nombre-corto=nombre-largo>

El nombre corto lo define el usuario. Si el espacio de nombres es propio, el nombre largo también lo define él. El único requisito que debe cumplir el nombre largo es ser una URI, un identificador único. Normalmente se suelen usar direcciones web pertenecientes al usuario, ya que es una forma sencilla de asegurar que es un identificador único. Por ejemplo, supongamos que se ha generado un fichero recomendaciones.xml, que contiene los datos del usuario (tomados de pedido.xml) junto con datos de discos y libros recomendados (tomados de novedades.xml). Para definir los dos espacios de nombres:

<recomendaciones xmlns:pedido="http://www.discolibro.com/pedidos"
  xmlns:novedad="http://www.discolibro.com/novedades">

Es importante tener en cuenta que las URL elegidas en realidad no tienen por qué existir, ni se accede a ellas durante el procesamiento del documento XML. Se tratan simplemente como identificadores.

1.3.2. Emplear espacios de nombres en un documento

Si se ha definido el atributo xmlns en una etiqueta del documento, dentro de ella podemos especificar que una subetiqueta pertenece a un espacio de nombres simplemente precediéndola de su nombre corto. Por ejemplo, si queremos diferenciar entre una <direccion> de pedido y una que se refiera a una URL, haríamos:

<datospedido>
  <pedido:direccion>C/ Cisne, 27 </pedido:direccion>
  <cliente> Pedro Pérez </cliente>
</datospedido>
...
<disco>
  <titulo> Supernatural </titulo>
  <interprete> Santana </interprete>
  <novedad:direccion>http://www.carlossantana.com</novedad:direccion>
</disco>

1.4. XPath

XPath es un lenguaje que permite acceder a una parte concreta de un documento XML. Podemos utilizarlo para buscar información en un documento (por ejemplo, mostrar el título de todos los discos del documento de novedades). Su sintaxis es similar a la que se usa en sistemas de ficheros estilo UNIX para expresar rutas de directorios (de ahí el nombre de Path). El problema de especificar cómo acceder a determinadas partes de un documento XML se puede ver como una versión más compleja de las rutas de directorios, ya que podemos modelar un documento XML como un árbol cuyos nodos son las etiquetas.

1.4.1. Trayectorias

La parte más importante del estándar XPath es la definición de trayectorias (location paths). En principio, las trayectorias parten por defecto del denominado nodo de contexto (que sería algo así como el equivalente al "directorio actual" en UNIX). Si el primer símbolo de la misma es una barra, entonces se considera que la trayectoria parte del nodo raíz del documento. Por ejemplo, la expresión XPath

/novedades/libro

Se refiere a las etiquetas

<libro>
  <titulo>Programación de aplicaciones web</titulo>
  <autor>John McCarra</autor>
  <autor>Frank Webini</autor>
  <direccion>http://www.paw.org</direccion>
</libro>
<libro>
 <titulo>Manolito Gafotas</titulo>
 <autor>Elvira Lindo</autor>
 <direccion>http://www.manolitogafotas.com</direccion>
</libro>

Como puede verse, una expresión XPath puede referirse a un conjunto de nodos (a diferencia de una ruta de directorios, que siempre se refiere solo a uno).

En general, una trayectoria está compuesta por un conjunto de pasos separados por un símbolo /. El conjunto de nodos seleccionados en un paso es la base para el paso siguiente. Además, en cada paso se puede aplicar opcionalmente un predicado (función booleana entre corchetes) que restringe el conjunto de nodos seleccionados. Así, el formato general de una trayectoria es

paso1[predicado1]/paso2[predicado2]...

Por ejemplo, para seleccionar los autores del primer libro del documento se haría

/novedades/libro[position()=1]/autor

Donde la función position() devuelve el orden que ocupa un nodo en un conjunto de nodos seleccionados.

En la siguiente tabla se resumen algunos de los operadores que permiten expresar caminos.

Operador Significado Ejemplo Resultado
// nodo hijo o descendiente /novedades//titulo (todos los títulos dentro de la etiqueta novedades - a cualquier nivel del árbol)
<titulo>O sea</titulo>
<titulo>Programación de 
        aplicaciones web</titulo> 
<titulo>Manolito Gafotas</titulo> 
* cualquier nodo /novedades/*/titulo
mismo que el anterior
@ atributo /novedades/disco/@genero
pop
. el propio nodo    
.. nodo padre /novedades/disco/titulo/..
<disco genero="pop">
   <titulo>O sea</titulo> 
   <interprete>
     Enrique Iglesias
   </interprete>
   <direccion>
    http://www.enriqueiglesias.com
   </direccion> 
</disco>
text() nodo de texto /novedades/disco/titulo/text()
O sea
processing-instruction() instrucción de procesamiento /processing-instruction()
<?xml-stylesheet 
     type="text/css" 
     href="novedades.css" ?>
comment() comentario    

1.4.2. Predicados

Se utilizan para restringir el conjunto de nodos seleccionados. Se seleccionarán finalmente aquellos que cumplan el predicado, que no es más que una expresión entre corchetes que devuelve un resultado booleano. XPath dispone de un conjunto de operadores y funciones bastante extenso que podemos emplear a la hora de definir predicados.

Operadores

En XPath hay operadores de distintos tipos:

Funciones

XPath tiene un gran número de funciones, algunas de las cuales se resumen en la tabla siguiente. En las siguientes funciones, el nodo de contexto es el nodo que se está evaluando en este momento, y la lista de nodos de contexto es el conjunto de nodos seleccionados.

Función Tipo de retorno Descripción
position() número devuelve la posición del nodo de contexto en la lista de nodos de contexto. Las posiciones empiezan por el 1.
last() número devuelve el número de nodos en la lista de contexto
count(conj-nodos) número devuelve el número de nodos en conj-nodos
local-name(conj-nodos) cadena devuelve la parte local (lo que viene detrás del prefijo de espacio de nombres) del primer nodo del conj-nodos. Si se llama sin argumentos opera sobre el nodo de contexto
generate-id(conj-nodos) cadena devuelve un identificador único para el primer nodo de conj-nodos. Si se usa sin argumento, lo devuelve para el nodo de contexto.

 

Podemos utilizar operadores y funciones para definir predicados. Por ejemplo

Expresión Significado
/novedades/disco[@genero='pop']
todos los discos de género pop
/novedades/disco[titulo='O sea']/interprete
el intérprete del disco titulado 'O sea'
/novedades/libro[position()=1]/autor[position()=last()]
el último autor del primer libro

 

1.4.3. Ejes

Las expresiones XPath vistas hasta el momento utilizan lo que se conoce como sintaxis abreviada. XPath tiene operadores más complejos, que permiten seleccionar cualquier conjunto de nodos relativo a una posición en el árbol. Estos operadores son los ejes, que se resumen en la tabla siguiente.

Eje Descripción
ancestor antepasado del nodo actual (padre, "abuelo, etc)
ancestor-or-self los anteriores contando también al propio nodo
attribute los atributos del nodo actual
child los hijos del nodo actual
descendant hijos, "nietos", etc, del nodo actual
descendant-or-self los anteriores contando también al propio nodo
following nodos que comienzan después que el nodo actual en el orden del documento (no se cuentan atributos ni declaraciones de espacios de nombres)
following-sibling nodos que comienzan después que el actual en el orden del documento y además tienen el mismo padre ("hermanos siguientes")
namespace espacio de nombres del nodo actual
parent nodo padre del nodo actual
preceding nodos que comienzan antes que el nodo actual en el orden del documento (no se cuentan atributos ni declaraciones de espacios de nombres)
preceding-sibling nodos que comienzan antes que el actual en el orden del documento y además tienen el mismo padre
self el nodo actual

La forma de utilizar los ejes es poner su nombre seguido de los caracteres :: y una expresión que restrinja los nodos seleccionados. Por ejemplo:

/novedades/disco/titulo/following-sibling::*

Seleccionaría todos (símbolo *) los "siguientes hermanos" (following-sibling) del título de los discos. En este caso, al mismo nivel que título vienen interprete y dirección, con lo que el resultado sería

<interprete> Enrique Iglesias </interprete>
<direccion>http://www.enriqueiglesias.com</direccion>

En la notación abreviada que hemos visto hasta ahora hay formas de expresar más brevemente algunos de los ejes. No obstante, otros ejes no tienen "traducción abreviada"

Notación abreviada Notación con ejes
/novedades/disco /child::novedades/child::disco
/novedades//titulo /novedades/descendant::titulo

 

1.5. XSLT

El intercambio de documentos XML a veces requiere además cambiar su formato. Por ejemplo, aunque dos organizaciones con distinto software utilicen XML para almacenar los mismos datos, pueden emplear etiquetas con distinto nombre o en distinto orden. O en otros casos, puede ser necesario generar documentos (como en el ejemplo del pedido en el apartado 1.3) que fusionen distintos documentos. XSLT es un lenguaje específicamente diseñado para transformar documentos XML, cambiando su estructura, fusionando documentos, o filtrando datos para generar nuevos documentos. Estos nuevos documentos pueden ser XML, pero también pueden emplear cualquier lenguaje de marcado (por ejemplo, HTML) o incluso pueden ser simplemente texto.

1.5.1. Estructura de un documento XSLT

Podemos considerar un documento XSLT como un programa, escrito con sintaxis XML, que especifica cómo procesar un documento XML para transformarlo en otro. El XSLT toma como entrada el documento a transformar en forma de árbol, y empieza a procesarlo por la raíz, descendiendo sucesivamente por los nodos. Un documento XSLT no es más que un conjunto de plantillas (templates) que especifican qué salida generar cuando el procesamiento alcanza un nodo determinado.

Por ejemplo, supongamos que se desea obtener un documento XML únicamente con los libros del ejemplo anterior. Además, de cada libro solo nos interesa el primer autor, que debe venir expresado con la etiqueta <autorPrinc> en lugar de <autor>. El siguiente es un documento XSLT que realiza la tarea:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <!--seguir procesando desde la raíz del árbol-->
   <xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>

<!-- ignorar la fecha --> <xsl:template match="fecha">
</xsl:template>

<!--ignorar los discos --> <xsl:template match="disco">
</xsl:template> <!-- de los libros, procesar únicamente el título y el primer autor -->

<xsl:template match="libro">
<libro>
<xsl:apply-templates select="titulo"/>
<xsl:apply-templates select="autor[position()=1]"/>
</libro>
</xsl:template>

<!-- en los títulos, generar la misma etiqueta --> <xsl:template match="titulo">
<titulo>
<xsl:value-of select="."/>
</titulo>
</xsl:template>

<!-- en los autores, generar la etiqueta autorPrinc --> <xsl:template match="autor">
<autorPrinc>
<xsl:value-of select="."/>
</autorPrinc>
</xsl:template>

</xsl:stylesheet>

Como puede verse, el documento sigue la sintaxis XML. Las etiquetas que son instrucciones propias del lenguaje XSLT vienen precedidas del prefijo de espacio de nombres xsl (este prefijo es arbitrario, y lo que importa es que en la raíz del documento esté asociado con el espacio de nombres "oficial" de XSLT).

1.5.2. Procesamiento de un documento XSLT

El procesamiento se realiza de la siguiente manera: el "intérprete" XSLT tiene en cada momento un nodo actual en el árbol del documento XML a transformar. Además hay una salida a donde se irá enviando el resultado del procesamiento. Este comienza por la raíz, buscando algún template cuyo match coincida con el nodo actual. En ese caso, se ejecutan las instrucciones de dentro del template, que básicamente pueden ser dos (aunque hay muchas más en el lenguaje):

El valor que puede tener un atributo match es más o menos el de una expresión XPath con notación abreviada (por ejemplo, no se pueden utilizar ejes). En los atributos select se puede colocar cualquier expresión XPath válida.

Cuando dentro de un template se pone texto o etiquetas que no pertenecen al espacio de nombres XSLT (en el ejemplo, no llevan el prefijo xsl), el intérprete xslt las copia en la salida.

1.5.3 Templates por defecto

En XSLT no es necesario escribir templates para procesar todos los nodos del documento XML original, ya que el estándar exige que el intérprete XSLT aplique una serie de templates por defecto para algunos casos (si el usuario no define templates para ellos). Estos casos son:

1.5.4. Generar etiquetas con atributos

Como se ha comentado, cualquier etiqueta o texto que no pertenezca al espacio de nombres de XSLT, se envía directamente a la salida. No obstante, si queremos generar etiquetas con atributos que tomen valor del documento XML original, podemos tener problemas. Por ejemplo, el siguiente template (que es incorrecto) intenta transformar la etiqueta "disco" en una etiqueta que contiene como atributo el título.

<xsl:template match="disco">
   <disco titulo="<xsl:value-of select="titulo">/>
</xsl:template>

El problema con el template anterior es que no es XML bien formado, ya que una etiqueta no puede ir "incrustada" dentro de otra. Por ello, hay una forma alternativa de expresar el select para estos casos que consiste en ponerlo entre llaves.

<xsl:template match="disco">
   <disco titulo="{titulo}"/>
</xsl:template>