JOOX è una libreria che permette di creare, leggere e manipolare documenti xml (può essere considerata un wrapper del package org.w3c.dom
) utilizzando delle fluent api, concatenando ad esempio una sequenza di operazioni in cascata.
Il funzionamento di JOOX è incentrato su poche classi di fondamentale importanza:
- JOOX
- Match
- Filter e FastFilter
- Each
Accesso JQuery-like
Dal momento che JOOX trae ispirazione da JQuery può essere utile definire, per chi ha avuto già a che fare con la famosa libreria javascript, delle modalità di accesso simili.
In particolare a partire da Java 5 è possibile utilizzare il costrutto “import static
” per importare una o più classi e procedere quindi all'invocazione dei metodi statici senza fare riferimento al nome della classe statica che li contiene.
Parsing di un documento xml
Per effettuare il parsing di un documento xml si usa il metodo $
della classe static JOOX.
Come detto è possibile accedere a tale metodo facendo riferimento direttamente alla classe JOOX (quindi JOOX.$(...)
) oppure una volta effettuato l'import static della classe senza fare riferimento a questa (quindi direttamente con $(...)
).
Quindi supponendo di voler effettuare il parsing di un documento xml memorizzato nel file documento.xml utilizzeremo la seguente istruzione:
Match m = $(new File("c:/documento.xml"));
Come vediamo il risultato di questa istruzione produce un'istanza della classe Match
che wrappa una lista ordinata di elementi DOM senza duplicati fornendo operazioni utili effettuabili su tutti gli elementi in essa contenuti.
Per gli esempi successivi utilizzeremo il seguente documento xml:
<documento>
<libri>
<libro id="1">
<nome>Io uccido</nome>
<autori>
<autore>Giorgio Faletti</autore>
</autori>
</libro>
<libro id="2">
<nome>I promessi sposi</nome>
<autori>
<autore>Alessandro Manzoni</autore>
</autori>
</libro>
<libro id="3">
<nome>Flatlandia</nome>
<autori>
<autore>Edwin Abbott</autore>
</autori>
</libro>
</libri>
<films>
<film id="1">
<nome>La vita è bella</nome>
<attori>
<attore>Roberto Benigni</attore>
<attore>Nicoletta Braschi</attore>
</attori>
</film>
</films>
</documento>
Funzioni di base (prima parte)
Alcune delle principali funzioni di base che è possibile applicare sulle istanze della classe Match
(e quindi su tutti gli elementi DOM in esse contenuti) sono:
isEmpty()
isNotEmpty()
size()
tag()
content()
Utilizzo delle classi Filter e FastFilter
A partire da una classe Match
è possibile selezionare un subset degli elementi DOM in essa contenuta (ottenendo una nuova istanza della classe Match) attraverso l'utilizzo delle funzioni filter e find:
filter
find
Entrambi questi metodi utilizzano come argomenti un Filter
(o FastFilter
) rappresentante le condizioni di selezione degli elementi del Match.
Alcuni dei filtri più comuni e semplici da utilizzare sono:
tag
estrae tutti gli elementi corrispondenti ad un dato tag
m.find(tag("libro"));
leaf
estrae tutti gli elementi che sono nodi-foglia
m.find(leaf());
ids
estrae tutti gli elementi che hanno id fra quelli passati come argomenti
m.find(ids(1,3));
odd
estrae gli elementi di posto dispari
m.find(tag("libro")).find(odd());
even
estrae gli elementi di posto pari
m.find(tag("libro")).find(odd());
E' possibile effettuare una composizione logica di due o più filtri mediante and, or, not come negli esempi seguenti:
m.find(or(tag("libro"), tag("film")));
m.find(and(tag("libro"), ids("1")));
m.find(and(tag("libro"), not(ids("1"))));
Funzioni di base (seconda parte)
Navigazione attraverso gli elementi DOM
Ovviamente attraverso JOOX è possibile navigare direttamente attraverso gli elementi del documento xml.
child(n)
recupera il figlio n-esimo di tutti gli elementi del Match
Match secondo_libro = m.find(tag("libri")).child(1);
next()
recupera l'elemento immediatamente successivo a ciascuno degli elementi del Match
Match terzo_libro = secondo_libro.next();
prev()
recupera l'elemento immediatamente precedente a ciascuno degli elementi del Match
Match primo_libro = secondo_libro.prev();
sibling()
recupera tutti gli elementi adiacenti a ciascuno degli elementi del Match
Match adiacenti = primo_libro.siblings();
parent()
recupera l'elemento padre di ciascuno degli elementi del Match
Match padre = primo_libro.parent();
parents()
recupera tutti gli antenati di ciascun elemento del Match
Match antenati = adiacenti.parents();
Modifica del documento xml
E' possibile infine modificare la struttura di un documento xml attraverso apposite funzioni:
prepend(...)
inserisce in testa al contenuto di ogni elemento DOM del Match
il codice xml passato come argomento
primo_libro.prepend("primo");
append(...)
inserisce in coda al contenuto di ogni elemento DOM del Match
il codice xml passato come argomento
primo_libro.append("ultimo");
append(...)
inserisce in coda al contenuto di ogni elemento DOM del Match
il codice xml passato come argomento
primo_libro.append("ultimo");
content(...)
costituisce il contenuto di ogni elemento DOM del Match
con il codice passato come argomento
primo_libro.content("contenuto");
attr(attributo, valore)
modifica il contenuto dell'attributo di ogni elemento DOM del Match
con quello passato come secondo argomento
secondo_libro.attr("attributo", "valore");
Eseguire una funzione per ogni elemento DOM di un Match
Per eseguire una funzione personalizzata per ogni elemento DOM all'interno dell'istanza di un Match è possibile utilizzare la funzione each(...)
passando come argomento l'istanza di una classe che implementa l'interfaccia Each
.
Tale interfaccia è sostanzialmente costituita da un metodo con la seguente firma:
public void each(Context context)
che viene invocato per ogni elemento del Match
Context
La seguente implementazione ad esempio stampa il contenuto testuale di tutti gli elementi del Match in grassetto:
public class BoldEach implements Each {
@Override
public void each(Context context) {
System.out.println(""+context.element().getTextContent()+"");
}
}
E viene utilizzata nel seguente modo:
istanza_match.each(new BoldEach());
In questo modo è particolarmente semplice l'implementazione di funzioni di parsing avanzate, concettualmente simili a dei callback: per esempi più sofisticati (utilizzo di xslt, associazione con ORM, etc) rimandiamo ad articoli successivi.