CERCA SITEMAP FEED RSS 1280
Ultimo aggiornamento: 30 Agosto 2009

Il linguaggio XPath

Le specifiche di XPath sono state promosse dal World Wide Web Consortium allo scopo di fornire un valido strumento per il recupero dei contenuti all’interno di un documento xml.
Fondamentalmente XPath si compone di espressioni che vengono di volta in volta valutate in un determinato contesto restituendo uno o più nodi del documento xml.
Un’espressione XPath è costituita da combinazioni di uno o più chunk di testo separati da caratteri speciali.
La maggior parte delle espressioni XPath individuano contenuti del documento xml preso in esame o parte di questi: tale processo avviene in uno o più passi, detti location step, che messi insieme danno vita ad un percorso completo di localizzazione ovvero una location path.
L’espressione /cartella/fascicolo ad esempio risulta essere costituita da due passi di localizzazione: il primo passo recupera gli elementi cartella figli dell’elemento root del documento xml, il secondo recupera gli elementi fascicolo figli degli elementi cartella recuperati al passo precedente; messi insieme costituiscono una location path completa.
Location Path
L’immagine a lato mostra il risultato della location path /cartella/fascicolo.

Come si può notare vengono selezionati soltanto i nodi-elemento fascicolo figli dei nodi-elemento cartella mentre tutti gli altri nodi presenti all’interno del documento vengono trascurati.
In base a quanto affermato, le espressioni XPath risultano essere costituite da token e delimitatori: i token in particolare sono insiemi di caratteri Unicode che assumono il significato di stringa se delimitati da virgolette ed il significato di nodi-elemento se privi di virgolette.
Le specifiche XPath prevedono l’utilizzo del carattere * come wildcard: il suo utilizzo permette di selezionare tutti i nodi del contesto indipendentemente dal loro nome.
Così l’espressione /cartella/* permette di selezionare indipendentemente tutti i nodi figlio degli elementi cartella presenti sotto l’elemento radice del documento xml.

I token di un’espressione XPath sono separati da opportuni delimitatori:
  • / separa un passo di localizzazione da un altro
  • [ ... ] permettono di definire un predicato (vedremo in seguito di cosa si tratta) che agisce sul contesto individuato dai location step che precedono il predicato stesso.
  • =, !=, %lt;, >, <=, >= vengono usati all’interno di un predicato per verificare il valore true o false del test applicato sul contesto (assumono rispettivamente il significato di uguale, diverso, minore, maggiore, minore o uguale, maggiore o uguale).
  • :: separa l’asse di un’espressione XPath (vedremo in seguito cosa si intende per asse) dal nome di uno specifico nodo.
  • //, @, ., .. costituiscono delle forme abbreviate per indicare rispettivamente i concetti di "se stesso o discendente", "attributo", "se stesso", "genitore".
  • | permette di definire un’espressione XPath composta dall’unione di più espressioni XPath
  • ( ... ) permettono di raggruppare sottoespressioni XPath o delimitare gli argomenti di una funzione XPath.
  • +, -, *, div, mod agiscono come operatori numerici col significato di somma, sottrazione, moltiplicazione, divisione e modulo.

Tipi di dati

XPath è in grado di trattare quattro tipi differenti di dati: stringhe, numeri, booleani e nodi.
Le stringhe sono insiemi di caratteri delimitati dalle virgolette: dal momento che le espressioni XPath sono spesso e volentieri utilizzate come valori di attributi spesso queste risultano essere delimitate da apici.
Le specifiche xml infatti prevedono utilizzo delle virgolette per delimitare il valore di un attributo.
Un tipo numerico è appunto un numero considerato in floating point sul quale è possibile applicare gli operatori numerici visti in precedenza ottenendo un risultato sempre di tipo numerico.
XPath definisce anche il valore speciale NaN (Not a Number) che può essere utilizzato per verificare se il valore di un elemento è numerico valido o meno:
string(number(mioelemento)) != "NaN" 
In XPath, come in qualasiasi altro linguaggio, un tipo booleano può assumere valore true o false e tale valore può essere convertito nella sua rappresentazione stringa o numerica utilizzando particolari funzioni.
L’uso principale del tipo booleano all’interno di un’espressione XPath riguarda la composizione dei predicati che esamineremo in seguito.
Per nodo infine si intende ciascun elemento di un documento xml.

Un nodo possiede generalmente un nome che può essere inteso sotto tre differenti aspetti:
  • QualifiedName (QName) indica il nome con il quale il nodo appare all’interno di un’istanza di un documento xml insieme eventualmente al prefisso del namespace di appartenenza.
  • Local-name indica il Qualified Name privato dell’eventuale prefisso del namespace di appartenenza.
  • L’expanded-name è invece costituito dalla coppia Uri associata al namespace di appartenenza e local-name.
Per node-set si intende un insieme di nodi identificati da un location step (o location path).

Se ad esempio si ha il seguente documento xml
<cartella>
   <documento>
      <sezione>sezione 1.1</sezione>
      <sezione>sezione 1.2</sezione>
   </documento>
   <documento>
      <sezione>sezione 2.1</sezione>
      <sezione>sezione 2.2</sezione>
   </documento>
</cartella> 
e si considera la seguente espressione XPath:
            
/cartella/documento/sezione
Il node-set selezionato sarà costituito da tutti gli elementi che hanno nome "sezione".
Il context-size indica il numero di nodi presenti all’interno di un node-set, il context-position invece indica la posizione di ciascun nodo all’interno del node-set di appartenenza.

Tipi di nodi

XPath è in grado di recuperare ogni tipo di contenuto presente all’interno di un documento xml: non solo elementi e attributi ma anche commenti, Processing Instruction, nodi testo ed altro ancora.
Le specifiche xml prevedono che l’intero documento sia contenuto all’interno di un’unico elemento detto radice (root): in XPath l’elemento radice è rappresentato dal carattere / e tutte le location path espresse a partire da tale elemento(tutte quelle che iniziano cioè col carattere /) sono considerate path assolute.
Ogni nodo elemento è generalmente composto da un tag di apertura, uno eventuale di chiusura e la porzione di documento xml compreso fra essi che a sua volta è costituito generalmente da altri nodi elemento.
I nodi attributo servono generalmente a specificare le proprietà degli elementi all’interno dei quali sono definiti.
Allo stesso modo sono definiti commenti, Processing Instruction, nodi-testo, namespaces ...
Ad ogni nodo è associata una corrispondente rappresentazione-testuale, detta string-value, che varia in base al tipo di nodo considerato:
  • Per l’elemento radice questa è data dalla concatenazione di tutti i nodi-testo presenti all’interno del documento xml.
  • Per un elemento lo string-value è costituito dalla concatenazione di tutti i nodi-testo compresi fra i tag di apertura e chiusura dell’elemento stesso.
  • Per gli attributi lo string-value è costituito dal valore normalizzato (ovvero privato di whitespaces ridondanti) dell’attributo stesso
  • Per le Processing Instruction lo string-value è dato dall’insieme di caratteri compresi fra il suo target di riferimento e il delimitatore di chiusura ?>
  • Per i commenti invece è costituito dal testo compreso fra i delimitatori di apertura <!-- e chiusura -->
  • Per i nodi-testo lo string-value è costituito dalle sezioni character-data (CDATA)
  • Ed infine per i namespaces lo string-value è dato dall’URI del namespaces associata all’eventuale prefisso.
Anche ad un node-set è associato uno string-value ma contrariamente a quanto si può pensare questo non è dato dalla concatenazione di tutti gli string-value dei nodi che appartengono al node-set ma corrisponde allo string-value del nodo che ha context-position pari a 1 (ovvero il primo nodo del node-set).

Location Step e Location Path

Come abbiamo detto la struttura tipica di una location path prevede dei token separati da delimitatori:
            
/cartella/documento/sezione/paragrafo
Essa risulta quindi composta da location step disposte in maniera tale che ad ogni passo (ovvero procedendo da sinistra verso destra della location path) vengono esclusi dei nodi dalla selezione e vengono considerati i rimanenti.
Per contesto si intende l’insieme dei nodi che ad un dato punto della location path sono selezionati.
Location Step e Contesto
L’immagine a lato riporta per ogni livello della struttura ad albero rappresentativa del documento il corrispondente location step che ne determina la selezione dei nodi.
A volte capita di dover selezionate contemporaneamente nodi individuati da distinte location path: in questo caso si ricorre all’utilizzo dell’operatore unione | per creare delle location path composte.
            
/cartella/documento/sezione/paragrafo | /cartella/documento/sezione/titolo

Location Step

La sintassi tipica di un location step prevede l’asse (opzionale), il nodetest (obligatorio) ed il predicato (opzionale) organizzati secondo la seguente sintassi:
            
asse::nodetest[predicato]
Il nodetest indica il tipo di nodi che vogliamo selezionare.
Esistono due possibili approcci: il primo prevede l’identificazione dei nodi in base al nome degli elementi, il secondo invece prevede l’identificazione dei nodi in base al tipo degli elementi.
Per selezionare gli elementi con un particolare nome è sufficiente indicare come nodetest il nome dell’elemento.

Così l’espressione
            
/documento
indica di selezionare tutti gli elementi figli dell’elemento radice che hanno nome pari a "documento" (come notiamo non abbiamo indicato nè l’asse nè alcun predicato ma solo il nodetest).
Come detto in precedenza per selezionare tutti gli elementi a prescindere dal loro nome è possibile utilizzare il carattere wildcard *.

L’espressione
            
/*
seleziona tutti i figli dell’elemento radice.
Per selezionare soltanto i nodi-testo possiamo utilizzare l’espressione text() come nell’esempio seguente:
            
/documento/text()
In tal modo selezioniamo i nodi-testo figli degli elementi che hanno nome "documento" (figli a loro volta dell’elemento radice)
Analogamente possiamo utilizzare node() per selezionare tutti i nodi indipendentemente dal tipo, comment() per selezionare i nodi-commento, processing-instruction() per selezionare le processing-instruction indipendentemente dal target e processing-instruction(target) per selezionare le processing-instruction aventi target indicato.
Attributi e namespace non possono essere selezionati facendo riferimento al solo nodetest ma occorre specificare l’asse.

L'asse

L’asse di un location step permette di indicare la direzione verso la quale vogliamo esaminare i nodi che abbiamo filtrato.

XPath definisce 13 tipi differenti di assi:
  • child:: individua il nodo immediatamente discendente dal nodo contesto.

    L’asse child è impostato di default infatti le espressioni
                
    /child::cartella/child::documento
    
    e
                
    /cartella/documento sono assolutamente equivalenti
    
  • parent:: individua il nodo genitore del nodo contesto.
  • descendant:: individua tutti i nodi discendenti del nodo contesto a qualsiasi profondità (i figli, i figli dei figli ...)
  • ancestor:: individua tutti gli antenati del nodo contesto fino all’elemento radice (il padre, il padre del padre ...)
  • descendant-or-self:: individua il nodo contesto stesso e tutti i suoi discendenti
  • ancestor-or-self:: individua il nodo contesto stesso e tutti i suoi antenati
  • following:: individua tutti i nodi che all’interno del documento xml seguono il nodo contesto esclusi i discendenti del nodo contesto
  • preceding:: individua tutti i nodi che all’interno del documento xml precedono il nodo contesto esclusi gli antenati del nodo contesto
  • following-sibling:: individua tutti i nodi che seguono il nodo contesto e che condividono con questo lo stesso nodo parent.
  • preceding-sibling:: individua tutti i nodi che precedono il nodo contesto e che condividono con questo lo stesso nodo parent.
  • attribute:: individua gli attributi del nodo contesto
  • namespace:: individua i nodi namespace
  • self:: individua il nodo contesto stesso.
Per gli assi attribute:: e discendant-or-self:: esistono le espressioni abbreviate @ e //

Le espressioni
            
/discendant-or-self::node()/ 
e
            
// 
sono equivalenti così come le espressioni
            
attribute::attributoelemento 
e
            
@attributoelemento 

I predicati

L’utilità dei predicati all’interno delle espressioni XPath sta nella possibilità di poter filtrare ulteriormente i nodi dal contesto correntemente selezionato in base all’impostazione di opportune condizioni.
Generalmente un predicato è racchiuso fra parentesi quadre ed espresso in termini di espressioni booleane del tipo:
            
valore1 operatore valore2 
dove valore1 e valore2 sono delle espressioni XPath e operatore è uno degli operatori booleani descritti in precedenza.
Ogni location path che appare all’interno del predicato fa riferimento al contesto stabilito dai location step che precedono il predicato stesso.
Per comprendere meglio questo concetto consideriamo il seguente documento xml:
            
<persona nome="Antonio"> 
   <fratello nome="Matteo"/> 
   <fratello nome="Marco"/> 
   <fratello nome="Luca"/> 
</persona>
e le espresioni:
            
/persona/fratello[@nome=’Matteo’] 
e
            
/persona[fratello/@nome=’Luca’] 
Il predicato della prima espressione fa riferimento al contesto determinato dall’expression path /persona/fratello quindi selezionerà il nodo elemento fratello che ha come attributo nome "Matteo".
Il predicato della seconda espressione invece fa riferimento al contesto determinato dall’expression path /persona quindi selezionerà l’elemento persona perchè avente un elemento-figlio fratello con attributo nome pari a "Luca".

Naturalmente è possibile definire all’interno di un predicato altri predicati innestati:
            
//persona[figlio[@nome=’Giovanni’]]  
La precedente espressione seleziona tutte le persone che hanno dei figli che hanno l’attributo nome pari a Giovanni.

Oppure è possibile comporre più predicati mediante operatori logici and e or.
            
//persona[figlio[@nome = ’Giovanni’] or figlio[@nome=’Matteo’]]   
In alcuni casi può sorgere la necessità di impostare dei predicati per verificare o meno l’esistenza di un determinato nodo figlio del contesto attuale.

In questi casi non si ricorre più ad una sintassi del tipo
            
valore1 operatore valore2   
ma viene specificato il solo valore1, così l’espressione:
            
//cartella[descendant::documento]   
seleziona solo gli elementi cartella che hanno almeno un documento come discendente.
Un caso particolare di predicati è rappresentato da quelli a valori numerici utilizzati per effettuare filtraggi sul contesto in base a quella che è la context-position occupata dai nodi selezionati ad un determinato momento.
Se si vuole selezionare il terzo elemento cartella di tutto il documento ad esempio è possibile utilizzare l’espressione:
            
//cartella[position()=3]
e, in alternativa, la forma contratta
            
//cartella[3]

Le funzioni XPath

Le funzioni XPath utilizzate generalmente all’interno dei predicati, assumono una struttura sintattica del tipo:
            
nomefunzione(param1, param2,..., param n)
ogni funzione ha un nome distinto ed accetta uno o più argomenti in base ai quali modifica il suo comportamento.
Le funzioni disponibili in XPath possono essere classificate in base al tipo del valore che restituiscono o in base al tipo degli argomenti sui quali queste operano.

funzioni Node-set

Una funzione node-set agisce su un node-set e restituisce un node-set.
  • last() restituisce il position-context dell’ultimo nodo del node-set corrispondente al contesto attualmente selezionato
  • position() restituisce il position context di un determinato nodo all’interno del node-set corrispondente al contesto attualmente selezionato
  • count(nodeset) restituisce il numero di nodi appartenenti ad un node-set, agisce come last() ma a differenza di quest’ultimo accetta in ingresso un’espressione XPATH che individua il node-set sul quale la funzione deve operare
  • id(anytype) restituisce un node-set costituito da tutti i nodi del contesto che hanno l’attributo id corrispondente al valore dell’argomento passato alla funzione: poiché l’id di un elemento deve essere univoco all’inetrno di un documento xml tale funzione restituisce un node-set composto da un solo nodo.
  • local-name(nodeset opzionale) restituisce il local name di un nodo, se non viene specificato alcun paramerto la funzione agisce sul contesto selezionato, se il nodeset specificato contiene più di un elemento la funzione restituisce il localname del primo nodo.
  • namespace-uri(nodeset opzionale) restituisce l’URI associata al namespace di un elemento o attributo, anche in questo caso se non viene specificato alcun parametro la funzione agisce sul contesto e se il parametro rappresenta un nodeset costituito da più nodi la funzione agisce sul primo di tali nodi.
  • name(nodeset opzionale) restituisce il Qname di un nodo, se non viene specificato alcun parametro agiscfe sul contesto. Se l’argomento rappresenta un node-set costituito da più nodi la funzione restituisce il Qname del primo nodo.

Funzioni stringa

Le funzioni stringa all’interno di Xpath rivestono un ruolo fondamentale in quanto permettono di creare contenuti con notevole flessibilità nuovi contenuti a partire da quelli già presenti.
  • string(anything opzionale) converte ogni argomento (opzionale) in una stringa di caratteri, se l’argomento è un nodeset la funzione restituisce la conversione in stringa del primo elemento (se è vuoto viene restituita una stringa vuota) , se è un numero viene restituita la corrispondente rappresentazione stringa se è un booleano restituisce le stringhe "true" o "false" a seconda del valore assunto dal booleano stesso.
  • concat(stringa1, stringa2, ..., stringan) concatena due o più stringhe in un’unica stringa
  • start-with(stringa1, stringa2) restituisce true se la prima stringa inizia con il valore della seconda, false viceversa.
  • contains(stringa1, stringa2) restituisce true le la prima stringa contiene la seconda, false viceversa
  • substring(stringa, numero1, numero2 opzionale) restituisce una sottostringa della stringa passata come parametro a partire dalla posizione indicata dall’argomento numero1.
    Opzionalmente è posibile indicare quanti caratteri devono essere estratti (numero2): se tale parametro non viene specificato vengono estratti tutti i caratteri a partire dalla posizione indicata da numero1.
    La prima posizione è rappresentata dal numero 1 e non dal numero 0 come avviene in molti linguaggi di programmazione.
  • substring-before(stringa1, stringa2) restituisce la porzione di stringa1 precedente alla prima occorrenza della stringa2 all’interno della stringa1.
    Se la stringa2 non appare la funzione restrituisce la stringa vuota.
  • substring-after(stringa1, stringa2) restituisce la porzione di stringa1 successiva alla prima occorrenza della stringa2 all’interno della stringa1.
    Se la stringa2 non appare la funzione restrituisce la stringa vuota.
  • string-length(string opzionale) restituisce il numero di caratteri della stringa passata come parametro, se il parametro non viene indicato la funzione agisce sullo string-value del contesto.
  • normalized-space(String opzionale) normalizza il contenuto della stringa eliminando caratteri whitespace estranei (ovvero quei caratteri che visivamente si traducono in spazi bianchi)
  • translate(stringa1, stringa2, stringa3) sostituisce i caratteri della stringa1 identificati dai caratteri presenti nella stringa2 con i caratteri della stringa3 che occupano la medesima posizione.

Funzioni booleane

Le funzioni booleane restituiscono tutte valori booleani.
  • boolean(anytype opzionale) converte il parametro passato alla funzione in un booleano in base alle seguenti regole:
    • se il parametro è una stringa restituisce true se la stringa ha una lunghezza maggiore di 0, false altrimenti.
    • Se il parametro è un numero legittimo (e non un NaN, not a number) restituisce false se il numero è pari a 0 e true altrimenti.
  • not(boolean) restituisce il negato del valore booleano passato come parametro
  • true() restituisce il valore true
  • false() restituisce il valoer false
  • lang(string) è strettamente legato all’utilizzo dell’attributo xml:lang: in particolare restituisce true se l’attributo è definito ed il suo valore comprende il valore della stringa passata come parametro, false altriemnti.

Funzioni numeriche

  • number(anytype opzionale) converte il parametro passato come argomento in un tipo numerico secondo le seguenti regole:
    • se il parametro è una stringa questa viene convertita in un numero solo se costituita da uno spazio opzionale, un segno meno opzionale, un altro spazio opzionale, un valore numerico ed un altro spazio opzionale: negli altri casi viene convertita in NaN.
    • se il parametro è un booleano viene convertito in 0 se pari a false in 1 se pari a true
    • se il parametro è un nodeset questo viene dapprima convertito in stringa e poi in numero
  • sum(nodeset) effettua la somma di tutti i valori dei nodi presenti convertiti in tipi numerici
  • floor(number) restituisce il numero intero immediatamente più piccolo del numero passato come argomento
  • ceiling(number) restituisce il numero intero immediatamente più grande del numero passato come argomento
  • round(number) restituisce il numero intero immediatamente più vicino a quello passato come argomento della funzione.