Tutorial de XPointer
Versión: 1.0, Enero, 2001

Autor: Victor Manuel Rivas Santos
Web: http://wwwdi.ujaen.es/~vrivas, Mail: vrivas@ujaen.es
inicioGeNeuracursos

(C) GeNeura Team
Web: http://www.geneura.org, Mail: todos@geneura.ugr.es

1. Introducción

Qué es y para qué sirve

XPointer es una extensión de Xpath que nos permite cargar en un visualizador de documentos XML solo aquellos elementos de un documento que nos interesen.

El equivalente en el mundo HTML es lo que se consigue con la etiqueta <A NAME="nombre">, la cual nos permite llamar a un documento con un nombre como: http://www.sitio.com/documento.html#etiqueta.

La idea que persigue XPointer es la misma, pero más potente. XPointer va a permitir añadir a una dirección del tipo http://www.sitio.com/documento.xml, la coletilla #xpointer(expresión), donde expresión es una expresión XPath, con algunas propiedades extra que no contempla el propio XPath.

Por desgracia, aún no hay muchas herramientas que soporten XPointer, de hecho es un estándar aún en discusión, así que me limitaré a decir sus características que sin duda pronto estarán disponibles.

2. Uso de XPointer

Como ya he indicado, XPointer es una extensión de XPath. Es imprescindible saber XPath para poder usarlo.

Una expresión XPointer se añade a un URI (Uniform Resource Identifier), como puede ser un URL (Uniform Resource Locator) o un URN (Uniform Resource Name).

La idea es añadir al final del URI lo siguiente:

#xpointer( expresion )

Un detalle muy importante a tener en cuenta es que se pueden concatenar expresiones XPointer que se evalúan de izquierda a derecha mientras devuelvan un conjunto vacío de nodos. Así, el siguiente ejemplo:

documento.xml#xpointer(/libro/capitulo[@public])xpointer(/libro/capitulo[@num="2"])

se buscaría por en primer lugar el conjunto de nodos delimitado por /libro/capitulo, y solo en el caso de que no existiese ninguno, se buscaría a continuación por /libro/capitulo[@num="2"].

3. Extensiones a XPath

Funciones

Una de las funciones que permiten localizar más rápidamente un elemento dentro de un documento XML es la función id(), la cual solo puede ser usada en aquellos ficheros XML que tengan elementos para los que se ha definido el atributo id y tienen asociado algún DTD que especifique que dicho identificador es único e irrepetible. (Un pequeño truco para evitar esto consiste en poner una expresión como: xpointer( id("p1" ))xpointer(//*[@id="p1"]) que funcionará tanto en el caso de que haya un DTD asociado - en cuyo caso solo devolverá un elemento - como en el caso de que no lo haya - aunque puede ser que existan más de un elemento con identificador p1.)

XPointer añade a la función id() las funciones here() y origin().

here() se utiliza en expresiones XPointer que apuntan a zonas internas del propio documento en que se utilizan dichas expresiones (normalmente este "apuntar" se hace mediante XLink), y hace referencia al nodo contexto desde el cual se hace la llamada mediante el URI. Así, por ejemplo, se puede usar en conjunción con following:: y preceding:: para realizar una mejor navegación a través de elementos del mismo tipo.

En cuanto a origin() se suele utilizar también en expresiones XPointer relacionads con enlaces de XLink, y refiere al elemento desde el cual se produjo el salto con el XLink.

Puntos y rangos(points y ranges, resp.)

XPath es una herramienta potente para seleccionar aquellas partes de un elemento XML que están perfectamente etiquetadas, pero no lo es tanto para seleccionar partes del documento XML que están dentro de un nodo texto. Por ejemplo, con XPAth podemos seleccionar perfectamente un nodo como <autor>Josefa Santos Rodríguez</autor>, pero es harina de otro costal el seleccionar la primera palabra del contenido de dicho elemento.

XPointer considera que entre cualesquiera dos caracteres consecutivos de texto de un documento XML, así como entre cada para de elementos también consecutivos, existe un punto. El fragmento de texto que existe entre dos puntos es un rango.

Para hacernos una idea, veamos cuántos puntos existen en el siguiente documento XML.


<saludo>
   Hola!
</saludo>

Pues bien, en total hay 13 puntos, a saber:

1. Antes del nodo root
2. Antes del nodo saludo
3. Antes del nodo texto Hola!
4. Antes del espacio en blanco que hay jusgto antes de Hola! y después de saludo
5. Antes de la letra H
6. Antes de la letra o
7. Antes de la letra l
8. Antes de la letra a
9. Antes de la letra !
10. Después de la letra !
11. Después del espacio que divide ! y /saludo
12. Después de /saludo
13. Después del nodo root.

Para usar los puntos en una expresión XPointer, hemos de recurrir a la función point() a la que hay que añadirle un predicado indicando qué punto en concreto deseamos seleccionar.

Seleccionamos el cuarto hijo del nodo root:

/point()[position()=4]

Seleccionamos la décima letra de un nodo texto:

/libro/autor/text()/point()[position()=10]

Rangos

Los rangos son sin duda también esenciales para trabajar con XPointer. Por ejemplo, un usario puede seleccionar con el ratón un trozo de un documento que puede perfectamente ir de la mitad de un párrafo hasta tres capítulos más abajo. Cualquier zona continua de un documento puede ser descrita con un rango. Un rango comienza en un determinado punto y finaliza en otro, cada uno de ellos determinado por una expresión XPath. Si el punto inicial determina no a un punto sino a un conjunto de nodos, entonces el primer punto de dicho conjunto se considera el punto inicial. E igual ocurre con el punto final: si su expresión XPath hace referencia no a un punto sino a un conjunto de nodos, se elegirá como punto final del rango el último punto de dicho conjunto de nodos.

Para especificar un rango añadimos la función /range-to(punto-final) a la expresión XPath del nodo inicial, siendo punto-final la expresión XPath del nodo final.

Funciones para rangos

XPointer incluye funciones para el tratamiento de rangos. La mayoría de ellas trabajan no con conjuntos de nodos, sino con con conjuntos de localizaciones, que en definitiva son conjuntos de nodos, más puntos, más rangos.

range(location-set) devuelve un conjunto de localizaciones por cada localización existente en el argumento. El rango es el mínimo necesario para cubrir la localización entera. Vamos, que convierte localizaciones en rangos.

range-inside(location-set) devuelve un conjunto de localizaciones conteniendo el interior de cada una de las localizaciones que se pasan como parámetro. Si una de las localizaciones es un elemento, devolvería su contenido, pero no el elemento en sí, pero si es un punto o un rango, devuelve dicho punto o rango.

start-point(location-set) devuelve un conjunto de localizaciones que contiene un punto que representa el primer punto de cada una de las localizaciones que se le pasan. Por ejemplo, start-point( //capitulo[1] ) devuelve un solo punto que es justo en al anterior al primer elemento de tipo capitulo, mientras start-point( //capitulo ) devuelve un conjunto de puntos, cada uno de ellos anterior a un elemento capitulo.

end-point(location-set) devuelve justo un conjunto de localizaciones que contiene posterior a cada una de las localizaciones que se le pasan.

Rangos de cadena

XPointer proporciona alguna capacidad muy básica para la selección de cadenas gracias a la función string-range(). Esta función necesita dos parámetros, en primer lugar un conjunto de nodos en el que buscar, y a continuación una cadena que buscar en ellos. Devuelve un conjunto de nodos que contiene un rango por cada aparición de la cadena buscada. Se pueden agregar dos parámetros opcionales, índice y longitud, para indicar cuántos caracteres pasado el emparejamiento se deben pasar para empezar el rango y cuántos caracteres debe tener éste.

La sintaxis definitiva es:

string-range( node-set, substring, index,length )

A continuación se muestran algunos ejemplos de lo que se puede hacer con esta función.

Buscar todas las ocurrencias de noche en el documento:

xpointer( string-range( /,"noche") )

Buscar todas las ocurrencias de calle en los titulos de los capitulos del documento:

xpointer( string-range(//capitulo/titulo,"calle") )

Al conjunto de nodos devuelto por la función string-range se le puede añadir un predicado que restrinja dichos nodos a los que cumplen ciertas condiciones.

Buscar solo la tercera de las ocurrencias de calle en los titulos de los capitulos del documento:

xpointer( string-range(//capitulo/titulo,"calle")[position()=3] )

Buscar la palabra que sigue a la primera ocurrencia de la palabra Ella:

xpointer(//capitulo/titulo,"Ella",4)[position()=1] )

El uso de la cadena vacía como cadena a buscar nos puede servir para recuperar el primer trozo de texto disponible en el conjunto de nodos:

xpointer( string-range( /,"" )[position()=1] )

Secuencias de hijos

Una secuencia de hijos es una abreviatura usada en XPointer para recuperar elementos por su posición, más que po su nombre. Por ejemplo: documento.xml#/1/3/4 haría referencia al 4 hijo (sea lo que sea), del tercer hijo, del primer hijo que cuelgue del nodo raíz.

En caso de que los elementos tengan identificadores, se pueden usar también estos atajos, de forma que #p1/4 haría referencia al cuarto hijo del elemento identificado como p1.

3. Referencias

Libros

Para realizar este tutorial me he basado básicamente en el capítulo 17 del siguiente libro:

- Elliotte Rusty Harold. XML Bible. Ed. Hungry Minds, Inc, 1999. (ISBN: 0764532367) (En Inglés)

* La versión actualizada de dicho capítulo puede obtenerse en la siguiente dirección web: ????, o también aquí en formato postscript.

El siguiente libro también está bien para abordar el estudio de XML, aunque el tema dedicado a XPointer es muy corto y poco significativo.

- Charles F. Goldbarg, Paul Prescod. The XML handbook. Ed. Prentice Hall PTR, 2000. (ISBN: 0-13-014714-1) (En Inglés)

Direcciones WEB

Las siguientes direcciones también pueden ser de gran ayuda:

* Utilidades para XPointer en el sitio de XMLSoftware: http://www.xmlsoftware.com/xlink/

* Recomendaciones del W3C sobre XPointer: http://www.w3.org/TR/xpath. Copia Local: referencias/xpointer-recommendations.html.