Cos'è
XPath
XPath definisce la sintassi e le regole tramite le quali
è possibile accedere alle informazioni contenute
all'interno di un documento XML, attraverso una rappresentazione
logica ad albero del documento stesso. Questo semplifica
maggiormente l'accesso ai nodi grazie a delle espressioni
che sono quasi identiche a quelle che siamo abituati
ad utilizzare quando stiamo lavorando su un file system
di un computer. Oltre a questo, XPath fornisce anche
una serie di funzioni che permettono di gestire e manipolare
i dati come numeri, stringhe e valori booleani.
Prendiamo,
ad esempio, un documento di test che rappresenta una
semplice raccolta di video, in differenti formati:
<?xml
version="1.0" encoding="ISO-8859-1"?>
<videoteca>
<video tipo="DVD">
<titolo>Titolo 1</titolo>
<anno>2000</anno>
<registi>
<nome>Regista 1</nome>
</registi>
<attori>
<nome>Attore 1</nome>
<nome>Attore 2</nome>
</attori>
</video>
<video tipo="VHS">
<titolo>Titolo 2</titolo>
<anno>2000</anno>
<registi>
<nome>Regista 2</nome>
</registi>
<attori>
<nome>Attore 3</nome>
<nome>Attore 4</nome>
</attori>
</video>
<video tipo="DVD">
<titolo>Titolo 3</titolo>
<anno>2001</anno>
<registi>
<nome>Regista 2</nome>
</registi>
<attori>
<nome>Attore 5</nome>
<nome>Attore 1</nome>
<nome>Attore 2</nome>
</attori>
</video>
<video tipo="VHS">
<titolo>Titolo 4</titolo>
<anno>2002</anno>
<registi>
<nome>Regista 3</nome>
</registi>
<attori>
<nome>Attore 3</nome>
<nome>Attore 5</nome>
</attori>
</video>
<video tipo="DVD">
<titolo>Titolo 5</titolo>
<anno>2003</anno>
<registi>
<nome>Regista 1</nome>
</registi>
<attori>
<nome>Attore 4</nome>
<nome>Attore 5</nome>
</attori>
</video>
<video tipo="VHS">
<titolo>Titolo 6</titolo>
<anno>2004</anno>
<registi>
<nome>Regista 1</nome>
<nome>Regista 3</nome>
</registi>
<attori>
<nome>Attore 1</nome>
<nome>Attore 2</nome>
</attori>
</video>
<video tipo="DVD">
<titolo>Titolo 7</titolo>
<anno>2004</anno>
<registi>
<nome>Regista 2</nome>
</registi>
<attori>
<nome>Attore 1</nome>
<nome>Attore 3</nome>
</attori>
</video>
</videoteca>
Vediamo,
in breve, le caratteristiche principali di questo linguaggio,
partendo proprio da modo utilizzato per localizzare
un nodo all'interno del documento.
XPath sfrutta delle espressioni che seguono lo stesso
pattern utilizzato per i percorsi all'interno del file
system dei computer, ossia una lista di elementi, separati
dalla barra "/". Il pattern seleziona l'elemento
che corrisponde a quel percorso. E così, applicato
al file di esempio, l'espressione "/videoteca/video/attori/attore"
indica tutti gli attori all'interno della videoteca.
Così come per i file system, se il percorso inizia
con la singola barra ("/"), rappresenta una
posizione assoluta, altrimenti ci si riferisce ad una
posizione relativa.
Diversamente dal file system, però, esiste anche
la possibilità di indicare un nodo, precedendolo
dalla doppia barra ("//"). In questo caso
verrebbero selezionati tutti gli elementi, indipendentemente
dalla loro posizione gerarchica. Se l'espressione "/videoteca/video/attori/nome"
serve per indicare tutti gli attori, l'espressione "//nome"
indicherebbe non solo gli attori, ma anche i registi.
Con XPath è possibile utilizzare la "wildcard",
ossia un carattere jolly che serve per indicare gli
elementi sconosciuti. Il pattern "/videoteca/video/*/nome"
indica tutti i registi e tutti gli attori, mentre l'espressione
composta "//*" seleziona tutti gli elementi
del documento.
La sintassi del lingaggio prevede anche la possibilità
di creare espressioni che sono in realtà composte
da più espressioni, separate fra loro dal carettere
"|" che significa, come spesso nel gergo informatico,
"or". Se volessimo selezionare, ad esempio,
tutti i registi e gli attori, si potrebbe ricorrere
all'espressione "/videoteca/video/registi/nome
| /videoteca/video/attori/nome" oppure "//registi/nome
| //attori/nome" o anche "/*/*/registi/nome
| /*/*/attori/nome".
Tutti queste espressioni servono per selezionare tutti
i nodi che corrispondono al pattern, ma è anche
possibile utilizzare le parentesi quadre ("[]")
per identificarne uno con precisione. Ad esempio, l'espressione
"/videoteca/video[2]/titolo" corrisponderebbe
al titolo del secondo video e, quindi, il risultato
sarebbe "Titolo 2".
L'ultimo carattere speciale all'interno delle espressioni
è il simbolo "@", che serve per identificare
un attributo all'interno di un elemento. Per selezionare
tutti i tipi di video presenti nel documento, si può
utilizzare un pattern come "/videoteca/video/@tipo",
oppure selezionare tutti i titoli dei dvd, attraverso
l'espressione "/videoteca/video[@tipo='DVD']/nome".
Se si volesse poi ottenere la lista di tutti gli attori
presenti nei vari video in formato VHS si potrebbe utilizzare
questo pattern "/videoteca/video[@tipo='VHS']//attori/nome".
A dire il vero, il "//" potrebbe anche essere
sostituito con la barra singola, ma è stato utilizzato
per mostrare che non deve necessariamente essere all'inizio
dell'espressione.
Come
accennato in precedenza, XPath fornisce anche una serie
di funzioni che permettono di ottenere ulteriori informazioni
contenute nel documento. Ad esempio è possibile
sapere il numero di elementi che corrispondono ad una
espressione attraverso l'uso della funzione "count()":
"count(/videoteca/video[@tipo='VHS'])" ritornerebbe
"3". mentre con "count(/videoteca/video[@tipo='DVD'])"
si otterrebbe "4".
Figura 1 - L'elenco delle funzioni di XPath è
accessibile all'interno delle specifiche, http://www.w3.org/TR/xpath#corelib
Così
come "count()", esistono altre funzioni che
operano direttamente sugli elementi del documento, come
"position()", che ritorna la posizione dell'elemento
all'interno della gerarchia, oppure come "last()",
che fornisce la posizione dell'ultimo elemento del documento
corrispondente al pattern indicato.
Ma in aggiunta a questa tipologia di funzioni, ne esistono
altre che consentono di manipolare numeri, stringhe
e valori booleani. Funzioni come "concat()"
per concatenare tra loro delle stringhe, come "round()"
che arrotonda un valore decimale o semplicemente di
conversione tra i vari tipi attraverso "string()",
"number()" o "boolean()".
L'elenco
delle funzioni, divise per tipologia, è disponibile
all'interno della pagina del W3Consortium che contiene
le specifiche del linguaggio, all'indirizzo http://www.w3.org/TR/xpath.
Commons
JXPath
Dopo aver introdotto le caratteristiche principali XPath,
si può cercare di capire come sia possibile mettere
in pratica la potenza di questo linguaggio attraverso
il componente Jakarta Commons JXPath (http://jakarta.apache.org/commons/jxpath/),
attualmente giunto alla versione 1.1.
Figura 2 - La homepage di Commons JXPath, all'indirizzo
http://jakarta.apache.org/commons/jxpath/
Come
sempre, prima di tutto, è necessario acquisire
il pacchetto, come sempre disponibile sia in versione
sorgente (all'indirizzo http://jakarta.apache.org/site/sourceindex.cgi)
che in versione binaria (http://jakarta.apache.org/site/binindex.cgi).
Se si conosce la sintassi di XPath, la fase di acquisizione
è senza dubbio la parte più complessa,
perché utilizzare il pacchetto è tanto
facile quanto potente.
L'esempio più semplice consiste nell'ottenere
alcune informazioni dal documento XML visto in precedenza:
import
java.net.*;
import org.apache.commons.jxpath.*;
import org.apache.commons.jxpath.xml.*;
public
class Esempio1 {
public
static final void main(String[] args) {
//
lettura documento XML all'interno di un container
URL docUrl = Esempio1.class.getResource("test.xml");
Container videoteca = new DocumentContainer(docUrl);
// creazione del contesto JXPath
JXPathContext context = JXPathContext.newContext(videoteca);
// Quanti sono in totale i video?
Double video = (Double)context.getValue("count(/videoteca/video)");
// e quanti VHS e DVD?
String quantiVhs = "count(/videoteca/video[@tipo
= 'VHS'])";
String quantiDvd = "count(/videoteca/video[@tipo
= 'DVD'])";
Integer dvd = (Integer)context.getValue(quantiVhs,
Integer.class);
Integer dvd = (Integer)context.getValue(quantiDvd,
Integer.class);
// risultato
System.out.println("Totale
video = " + video.toString());
System.out.println(" VHS:
" + vhs.toString());
System.out.println(" DVD:
" + dvd.toString());
}
}
Le
prime tre righe di codice sono il cuore di questo piccolo
esempio.
Prima
di tutto viene definito il "Container", un
oggetto che fornisce un meccanismo per accedere al contenuto
del documento XML in modo completamente trasparente.
URL
docUrl = Esempio1.class.getResource("test.xml");
Container videoteca = new DocumentContainer(docUrl);
Il
documento, in questo caso passato sottoforma di URL,
viene passato come argomento al costruttore del contenitore
e non verrà letto ed interpretato sino a quando
non sarà ritenuto necessario a seguito di una
esplicita richiesta.
A questo punto, per poter accedere alle informazioni
contenute nel documento attraverso l'uso del linguaggio
XPath, è necessario creare il JXPathContext,
ossia il contesto che si occupa di interpretare le nostre
richieste e fornire le risposte.
JXPathContext
context = JXPathContext.newContext(videoteca);
A
questo punto abbiamo lo strumento per poter utilizzare
il linguaggio XPath all'interno di una applicazione
Java, ad esempio utilizzando il metodo "getValue()".
Per vedere quanti siano in totale i video della videoteca
basta utilizzare l'espressione "count(/videoteca/video)":
Double
video = (Double)context.getValue("count(/videoteca/video)");
Il
metodo ritorna un oggetto il cui tipo dipende dall'espressione
utilizzata. Un numero come in questo caso viene restituito,
di default, sottoforma di Double. Esiste però
la possibilità di chiedere a JXPath di ritornare
il valore in un tipo differente, semplicemente fornendo
la classe che si intende ottenere, come nell'esempio,
dove il numero di video in formato vhs o dvd sarà
ritornato come Integer:
String
quantiVhs = "count(/videoteca/video[@tipo = 'VHS'])";
String quantiDvd = "count(/videoteca/video[@tipo
= 'DVD'])";
Integer dvd = (Integer)context.getValue(quantiVhs, Integer.class);
Integer dvd = (Integer)context.getValue(quantiDvd, Integer.class);
Il
programma termina poi stampando a video i risultati
ottenuti.
Le espressioni, però, non sempre ritornano un
valore singolo come si è visto in questo esempio
e vi sono infatti situazioni in cui al pattern XPath
corrisponde più di un nodo o di un elemento.
In questi casi, "getValue()" ritornerebbe
un solo valore, ossia il "primo" trovato,
ignorando gli altri. Se provassimo questa linea di codice:
String
val = (String)context.getValue("//nome");
otterremmo
solo il primo valore, ossia "Regista 1". Per
poter accedere a tutti i valori che corrispondono a
quel pattern è necessario utilizzare il metodo
"iterate()", come in questo secondo esempio:
import
java.net.*;
import java.util.*;
import org.apache.commons.jxpath.*;
import org.apache.commons.jxpath.xml.*;
public
class Esempio2 {
public static final void main(String[] args)
{
// lettura documento XML all'interno
di un container
URL docUrl = Esempio1.class.getResource("test.xml");
Container videoteca = new DocumentContainer(docUrl);
// creazione del contesto JXPath
JXPathContext context = JXPathContext.newContext(videoteca);
// Lista video VHS
System.out.println("VHS:");
Iterator iter;
iter = context.iterate("/videoteca/video[@tipo
= 'VHS']/titolo");
while (iter.hasNext()) {
String titolo =
(String)iter.next();
System.out.println("\t"
+ titolo);
}
// Lista video DVD
System.out.println("DVD:");
iter = context.iterate("/videoteca/video[@tipo
= 'DVD']/titolo");
while (iter.hasNext()) {
String titolo =
(String)iter.next();
System.out.println("\t"
+ titolo);
}
}
}
Il
metodo "iterate()" ritorna sempre un java.util.Iterator
contenente tutti i valori che corrispondono all'espressione
indicata. In questo codice viene utilizzato prima per
raccogliere tutti i titoli dei video in formato vhs
e, quindi, dvd.
Teoricamente
l'articolo potrebbe chiudersi qui dopo questa veloce,
ma semplicissima, analisi di come JXPath sia l'interfaccia
Java allo standard XPath. Invece...
Non
solo XML
Il gruppo che si occupa del progetto e dello sviluppo
di questo componente, ha voluto andare oltre, pensando
che la stessa identica logica di XPath poteva anche
essere applicata agli oggetti Java, in particolare ai
JavaBeans. Prendiamone uno:
public
class Video {
// attributi
private String tipo;
private String titolo;
private int anno;
private String[] attori;
// getters and setters
// tipo
public void setTipo(String arg) {
tipo = arg;
}
public
String getTipo() {
return tipo;
}
// titolo
public void setTitolo(String arg) {
titolo = arg;
}
public
String getTitolo() {
return titolo;
}
// anno
public void setAnno(int arg) {
anno = arg;
}
public
int getAnno() {
return anno;
}
}
Questo
Javabean rappresenta un video, per ora caratterizzato
solo da tipo, titolo e anno di produzione. Ora è
tempo di scrivere una piccolissima applicazione di prova:
import
org.apache.commons.jxpath.*;
public
class Esempio3 {
public static final void main(String[] args)
{
// crea e popola un video
Video video = new Video();
video.setTipo("DVD");
video.setTitolo("Titolo
1");
video.setAnno(2004);
// creazione del contesto JXPath
JXPathContext context = JXPathContext.newContext(video);
// estrai il titolo e mostralo
a video
String val = (String)context.getValue("/titolo");
System.out.println(val);
}
}
Le
prime righe servono per costruire l'oggetto, popolandolo
con dei dati semplici. A questo punto, l'oggetto viene
utilizzato per creare un nuovo contesto JXPath. Come
si può intuire, il JavaBean può tranquillamente
essere considerato un contenitore a tutti gli effetti,
esattamente come il file XML e, come tale, è
possibile accedervi secondo la sintassi XPath.
Nell'esempio viene estratto e mostrato a video l'attributo
"titolo".
Figura 3 - Il Javadoc della classe JXPathContext,
che contiene alcune informazioni ed esempi relativi
all'uso del componente. Disponibile all'indirizzo http://jakarta.apache.org/commons/jxpath/apidocs/
Ma
XPath non si ferma solo all'interrogazione, visto che
attraverso il metodo "setValue()" si puo definire
un valore all'interno del container:
import
org.apache.commons.jxpath.*;
public
class Esempio4 {
public static final void main(String[] args)
{
// creazione del contesto JXPath
JXPathContext context = JXPathContext.newContext(new
Video());
// popola il bean usando JXPath
context.setValue("/tipo",
"DVD");
context.setValue("/titolo",
"Titolo 1");
context.setValue("/anno",
new Integer(2004));
String
val = (String)context.getValue("/titolo");
System.out.println(val);
}
}
In
questo caso il contesto viene creato subito e come argomento
viene fornito un nuovo oggetto di tipo video. Quindi,
attraverso "setValue()" si popola il container
con i dati per poi sceglierne uno per verificare che
tutto sia andato come previsto.
Oggetti
più complessi
Il JavaBean utilizzato per l'esempio è molto
semplice visto che è composto solo da String
e int ma, come è possibile intuire, gli oggetti
di questo genere non sono frequenti. Per complicare
lievemente la situazione si può iniziare a creare
un oggetto di tipo Persona, che abbia il solo attributo
"nome", che potremo utilizzare sia per i registi
che per gli attori:
public
class Persona {
String nome;
public Persona() {
// costruttore nullo
}
public
Persona(String nome) {
setNome(nome);
}
public void setNome(String arg) {
nome = arg;
}
public
String getNome() {
return nome;
}
}
Questo
oggetto verrà utilizzato all'interno del bean
Video, per definire gli attori ed i registi, semplicemente
aggiungendo gli attributi:
private
List registi = new ArrayList();
private List attori = new ArrayList();
ed
il codice per accedervi (getters and setters):
// attori
public void setAttori(Persona arg) {
attori.add(arg);
}
public List getAttori() {
return attori;
}
public
Persona getAttori(int idx) {
return (Persona)attori.get(idx);
}
// registi
public void setRegisti(Persona arg) {
registi.add(arg);
}
public List getRegisti() {
return registi;
}
public
Persona getRegisti(int idx) {
return (Persona)registi.get(idx);
}
Il
nuovo esempio definirà anche due registi e due
attori, quindi stamperà il titolo, il secondo
regista e l'elenco degli attori:
import
org.apache.commons.jxpath.*;
public
class Esempio5 {
public static final void main(String[] args)
{
// crea e popola un video
Video video = new Video();
video.setTipo("DVD");
video.setTitolo("Titolo
1");
video.setAnno(2004);
video.setRegisti(new Persona("Regista
1"));
video.setRegisti(new Persona("Regista
2"));
video.setAttori(new Persona("Attore
1"));
video.setAttori(new Persona("Attore
2"));
// creazione del contesto JXPath
JXPathContext context = JXPathContext.newContext(video);
// prendi e stampa il titolo
String val = (String)context.getValue("/titolo");
System.out.println(val);
// prendi e stampa il secondo
regista
val = (String)context.getValue("/registi[2]/nome");
System.out.println(val);
// prendi e stampa gli attori
Iterator iter = context.iterate("//attori/nome");
while (iter.hasNext()) {
System.out.println(iter.next());
}
}
}
In
una situazione più realistica, voi potreste avere
tutte le vostre informazioni riguardo ai video all'interno
di un database e, magari, sareste interessati ad avere
una pagina di informazioni sulla vostra videoteca contenuti
all'interno di una java.util.List o di un semplice array
di oggetti.
In questo caso è necessario creare il JXPathContext
basandosi sull'oggetto che contiene la lista (il "container",
appunto) che viene considerata poi la "root"
(ossia la radice principale) per XPath.
Conclusioni
A mio parere, il punto di forza di questo componente
consiste nella possibilità offerta a tutti gli
sviluppatori di sfruttare il linguaggio XPath per accedere
a oggetti Java, oltre che ai documenti XML, per il quali
è stato sviluppato. Proprio questa sua versatilità
pone Jakarta Commons JXPath ad un livello differente
rispetto agli altri componenti Jakarta, visti nei mesi
scorsi, senza per questo esserne in competizione diretta.
Sia Digester che Betwixt sono stati progettati e sviluppati
solo ed esclusivamente per gestire i documenti in formato
XML secondo una logica lievemente differente da quella
offerta da JXPath che, oltretutto, ha un campo di azione
molto più ampio.
A
chi già conosce XPath consiglio vivamente di
investire una po' del proprio tempo per analizzare e
valutare le potenzialità offerte da JXPath, in
particolare alla possibilità di utilizzarlo al
di fuori del mondo strettamente XML. Ma questo consiglio
è diretto anche a tutti coloro che non conoscono
ancora questo linguaggio, indipendentemente dalla necessità
di lavorare con documenti costruiti seguendo lo standard
XML.
Figura 4 - Frammento della pagina principale del
tutorial per XPath offerto da W3Schools, all'indirizzo
http://www.w3schools.com/xpath/default.asp
In
questo articolo, purtroppo, non abbiamo avuto la possibilità
di analizzare con maggior attenzione il discorso XPath
che è molto più ampio, considerato anche
che è nato per essere utilizzato all'interno
di altre tecnologie, come ad esempio l'eXtensible Stylesheet
Language(XSL).
Se voleste approfondire la vostra conoscenza del linguaggio
potete sempre utilizzare le specifiche pubbliche, oppure
seguire il veloce e semplice corso online di "W3Schools",
presente, insieme ad altri, all'indirizzo http://www.w3schools.com/.
|