Interazione tra Java ed XML con JDOM

In questo articolo viene analizzato con diversi esempi di codice come utilizzare JDOM per far parlare le nostre applicazioni Java in XML

JDOM è una libreria java open source per la creazione e manipolazione di file xml molto potente e semplice da usare. Per scaricare la libreria, la documentazione ufficiale e qualche esempio potete visitare il sito ufficiale di JDOM, http://www.jdom.org.

Per questo articolo ho utilizzato JDOM 1.0, scaricabile all’indirizzo http://www.jdom.org/dist/binary/jdom-1.0.zip. Per compilare avrete bisogno solo del file jdom.jar (presente nella cartella build, dentro l’archivio da scaricare). Sempre nell’archivio linkato qui sopra trovate la documentazione javadoc ed i sorgenti (essendo JDOM open source, come già detto).

Per gli esempi che ho preparato per questo articolo mi appoggerò su un file xml con questa struttura:

N.B.: per utilizzare le classi di JDOM, dovrete importare il package org.jdom (e sotto-package, quando occorre), assicurandovi che il file jdom.jar sia presente nel vostro classpath!

 

Compilare ed eseguire programmi con JDOM

Dalla shell (o dall’emulatore del dos) scrivete:

ad esempio:

dove il . (punto) indica la directory corrente.

Nel caso java o javac (od entrambi) siano comandi non validi, assicuratevi di aver installato correttamente l’sdk (se non è presente nel vostro sistema, scaricatelo da http://www.java.sun.com) ed eventualmente modificate le variabili d’ambiente giuste ($PATH in linux, qualcosa di simile – presumo – per windows).

N.B. 1: sia in java che in javac dovrebbe essere utilizzabile il comando -cp per indicare il classpath. Nella mia versione dell’sdk (1.4.2 per linux) -cp va bene solo per java… Controllate il comando corretto lanciando java e javac senza argomenti.

N.B. 2: in ambiente linux, anzichè usare ; (punto e virgola) per separare l’opzione del classpath dal resto degli argomenti bisognera’ usare : (due punti). Su linux infatti il punto e virgola è usato per dare due o piu’ comandi con una stessa istruzione.

 

Creare e gestire gli elementi: aggiungere testo, attributi e figli

Prima di scrivere dati nel file, dovrete prepare gli elementi. Vediamo come fare. Un qualsiasi elemento xml (come ‘archivio_cd’, ‘album’, ‘tracce’, ecc.) e’ rappresentato in JDOM da un oggetto della classe org.jdom.Element. Per esempio, ecco l’elemento ‘album’:

Per aggiungere attributi all’elemento, dobbiamo chiamare su quell’istanza il metodo setAttribute nel seguente modo:

dove il primo parametro è il nome dell’attributo, mentre il secondo è il valore dello stesso. Entrambi devono essere stringhe. Non c’è limite al numero di attributi inseribili in un Element, basta che abbiano nomi diversi.

Attenzione! Durante la compilazione non vengono eseguiti controlli sulla presenza di doppioni tra gli attributi, attenzione quindi! Per leggere il valore di un attributo invece, si utilizza il metodo getAttributeValue:

L’argomento di getAttributeValue ovviamente è il nome dell’attributo di cui vogliamo leggere il valore. Nel caso l’attributo non esistesse, verrebbe restituito null. Il valore dell’attributo è restituito come stringa. Se abbiamo bisogno di quel valore come tipo primitivo dobbiamo usare le classi wrapper di java.lang:

Per aggiungere del testo ad un elemento (come per esempio, aggiungere all’elemento ‘titolo’ il titolo dell’album) si utilizza il metodo setText. La stringa passata andrà a comporre il testo dell’elemento.

Come per gli attributi, si può ottere il testo di un elemento:

getText restituisce il testo di un elemento cosi’ come viene letto, mentre getTextTrim restituisce il testo di un elemento dopo che sono stati tolti gli (eventuali) spazi iniziali e finali (ed è la stessa cosa di getText().trim()). Se andate di fretta, potete ottenere il testo di un elemento direttamente dall’elemento genitore:

Questo codice restituisce il testo dell’elemento figlio di ‘elementoAlbum’ che si chiama ‘gruppo’. La seconda versione toglie gli spazi iniziali e finali eventualmente presenti. Se vogliamo aggiungere ad un elemento dei sotto elementi, dobbiamo chiamare su quell’elemento il metodo addContent:

Come aggiungete elementi, li potete anche togliere. Basta chiamare il metodo removeContent sull’elemento a cui dobbiamo togliere un sotto elemento, passando come argomento l’Element da rimuovere.

Dato poi un oggetto Element, potrete sempre ottenere sia l’elemento genitore che gli eventuali elementi figli. Ovviamente la radice ha l’elemento genitore null. Per ottenere l’elemento genitore basta chiamare il metodo getParentElement (ereditato da org.jdom.Content) sull’elemento che ci interessa:

Per ottenere gli elementi figli invece, si chiama il metodo getChildren, anch’esso ereditato da Content:

In questo modo ‘elementiFigli’ conterrà l’elenco di tutti i figli (di primo livello) dell’oggetto chiamante. Se si desidera filtrare il risultato, per ottenere solo un certo tipo di elementi figli, dobbiamo passare a getChildren una stringa con il nome degli elementi che si desidera ottenere:

Dopo l’esecuzione di questo codice, ‘elementiFigli’ conterrà tutti i figli di ‘elemento’ che si chiamano ‘traccia’. I figli che non soddisfano il “filtro” verranno ignorati.

N.B.: il valore restituito da getChildren non sarà mai null! Infatti, nel caso l’elemento non abbia figli (o ne ha ma non corrispondono al filtro) viene restituita una lista vuota.

Da un elemento si può poi ottenere un unico elemento figlio:

In questo semplice esempio, getChild restituisce il primo elemento figlio di ‘elemento’ che si chiama ‘titolo’. Questa possibilità è utile quando siamo sicuri che un certo elemento ha un unico figlio con quel particolare nome.

 

Creare il documento

Dopo aver caricato l’albero xml con degli elementi, bisogna creare un oggetto org.jdom.Document che vada a contenere il tutto, a partire da un nodo detto radice (il primo elemento che appare all’inizio di un file xml). Si può agire in due modi:

  1. si passa l’elemento radice al costruttore di Document;
  2. si crea un Document usando il costruttore senza parametri e si imposta successivamente la radice;

 

 

Scrivere su file

Dopo aver preparato l’oggetto Document, siamo pronti per scriverlo su un file. Per fare ciò abbiamo bisogno di un oggetto della classe org.jdom.output.XMLOutputter e di un OutputStream:

Il metodo setFormat serve ad indicare all’oggetto XMLOutputter in che modo verrà formattato il file che si sta creando. Di default tutto il contenuto sarà scritto senza indentazione su un unica riga (e sarà abbastanza illeggibile). Format.getPrettyFormat farà in modo che si vada a capo dopo ogni elemento e che gli elementi figli vengano indentati correttamente (volendo si può modificare a piacere il valore di indentazione).

N.B. 1: il metodo output di XMLOutputter accetta un qualsiasi java.io.OutputStream e java.io.Writer
N.B. 2: in alcune versioni di JDOM è possibile, se il file su cui scrivere non esiste, che questo non venga creato! Nel caso questo succedesse provvedete voi a crearlo con il metodo createNewFile di un oggetto java.io.File.
N.B. 3: ogni volta che l’albero xml viene modificato e volete modificare anche il contenuto del file, l’unica soluzione è la riscrittura completa di tutto l’albero: JDOM non permette di modificare il file solo dove è necessario, ma lo riscrive completamente. Perciò, in caso stiate manipolando grandi quantità di dati, evitate di far riscrivere il tutto ogni dieci secondi perchè ci perdete un sacco di tempo… 😉

 

Leggere da file

Per leggere il contenuto di un file xml dobbiamo creare un istanza della classe org.input.SAXBuilder. Se non si verificheranno errori otterremo il contenuto del file all’interno di un oggetto Document.

L’unica cosa da fare sarà istanziare un SAXBuilder e chiamare il metodo build passando come parametro il File che rappresenta il file xml che vogliamo leggere. In caso di errori durante la lettura verrà sollevata una IOException, mentre se l’errore è a livello dell’xml verrà lanciata una JDOMException (per esempio, se il contenuto non è well-formed).

Dopo aver ottenuto il Document, possiamo ottenere l’elemento radice attraverso il metodo getRootElement:

In questo modo possiamo ottenere facilmente ogni altro nodo, attraverso i metodi visti in precedenza.

 

Esplorare la struttura del file xml caricato

Abbiamo visto che quando si usa getChildren su un elemento per ottenerne gli elementi figli, otteniamo questi (se presenti) all’interno di una lista. Come in qualsiasi lista degna di tale nome, non possiamo accedere direttamente all’elemento che ci interessa, ma dobbiamo scorrere gli elementi uno ad uno.

Java ci viene in aiuto fornendoci degli oggetti particolari, gli iteratori, che si occupano di scorrere per noi una qualsiasi Collection (come la List, ad esempio, ma non solo!). Usare un iteratore è molto semplice, come mostra questo esempio:

Se per esempio volessimo visualizzare sulla console il contenuto del nostro archivio cd, possiamo creare un metodo simile

Ecco altre semplici metodi, a titolo d’esempio:

 

Lavorare con più alberi XML

Può capitare di dover lavorare con più alberi xml all’interno dello stesso programma e di dover far interagire gli elementi dei vari alberi, ad esempio facendo degli scambi. La cosa di per sè non e’ complicata ma dobbiamo prestare attenzione ad una cosa: in JDOM ogni elemento può avere al massimo un unico genitore. Per questo, il seguente codice genera un errore a runtime:

Sto cercando di aggiungere ‘elemento2’ ad ‘elemento1’, ma ‘elemento2’ ha già un genitore!

Il modo più semplice per risolvere il problema consiste nell’usare il metodo detach: questo metodo letteralmente “stacca” l’elemento che lo chiama dal proprio elemento genitore, permettendoci di spostarlo da altre parti senza problemi.

 

Conclusioni

Spero che questo semplice tutorial vi sia stato utile 🙂

Tuttavia, se volete approfondire la vostra conoscenza su JDOM non posso non consigliarvi di dare un occhiata alla documentazione ufficiale. E’ stata creata con javadoc ed è veramente chiara e completa, con molti esempi ed utili consigli (tutto in inglese purtroppo).

Troverete un sacco di classi e metodi che per brevità non ho illustrato in questo articolo ma che possono essere molto interessanti. Ad esempio, c’è la possibilità di creare DTD, namespace e gestire diverse funzionalità avanzate che probabilmente nemmeno conoscevate 🙂

In ogni caso, per qualunque chiarimento non esitate a scrivermi!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *