MokaByte 67 - 8bre 2002 
JDOM
Una potente API per usare XML con Java
di
Andrea Giovannini
In [2, cap. 13] sono state presentate varie tecniche di elaborazione di documenti XML fra le quali SAX, DOM e JDOM. SAX e DOM sono standard ed esistono quindi diverse implementazioni in vari linguaggi (Perl, Python, linguaggi COM e .NET, …). In questo articolo si amplierà ed aggiornerà la sezione presentata in [2, cap. 13] su JDOM [1], una API sviluppata appositamente per gestire documenti XML in Java

JDOM è una API open source, sviluppata da Brett McLaughlin e Jason Hunter, giunta al momento alla versione beta 8. L'idea alla base di JDOM è quella di fornire uno strumento per la gestione di documenti XML con i meccanismi che si aspetterebbe uno sviluppatore Java.
Si osservi che nonostante JDOM sia rivolto agli sviluppatori Java è comunque una API che interagisce con SAX e DOM; i programmi sviluppati con JDOM possono quindi ricevere in input documenti XML da un parser SAX/DOM e analogamente possono generare un documento DOM o uno stream di eventi SAX.

 

Un documento JDOM
Un documento XML viene rappresentato come un'istanza della classe org.jdom.Document; JDOM fornisce anche l'analogo delle interfacce DOM come classi Java ma si osservi che la gerarchia di classi JDOM non ha alcuna relazione diretta con DOM, ad esempio la classe Node di JDOM non implementa l'interfaccia Node di DOM.
La prima caratteristica Java-oriented di JDOM che lo sviluppatore incontra è il poter creare gli oggetti direttamente col proprio costruttore, senza la necessità di utilizzare metodi factory. Per creare ad esempio un oggetto org.jdom.Element che rappresenta un tag <Document> è quindi sufficiente scrivere

Element e = new Element("Document");

 

Parsing
Per il parsing di documenti XML JDOM fornisce i builder, oggetti che costruiscono un documento a partire da diverse sorgenti dati come file, InputStream e InputSource. Sono definiti due tipi di builder, DOMBuilder e SAXBuilder. Come è evidente dal nome DOMBuilder carica un documento a partire da un oggetto org.w3c.dom.Document mentre SAXBuilder sfrutta un parser SAX ed è quindi più performante.

Il seguente codice mostra come usare un SAXBuilder per eseguire il parsing di un file istanziando un documento JDOM:

SAXBuilder builder = new SAXBuilder(true);
Document doc = builder.build(new File(args[0]));

// ...

Le classi SAXBuilder e DOMBuilder hanno vari costruttori in overloading che permettono di specificare quale parser usare (il default è Xerces) e se eseguire o meno la validazione del documento (il default è false).

 

I/O di documenti JDOM
Avere un potente framework per l'elaborazione di documenti XML è importante ma è altrettanto importante gestire l'I/O di documenti in maniera pratica.
Per quanto riguarda l'input di documenti le varie versioni in overloading del metodo build() coprono un ampio spettro di possibilità. La classe DOMBuilder permette inoltre di creare un documento JDOM a partire da un documento DOM.
L'aspetto più interessante riguarda la gestione dell'output che, come visto, in DOM è abbastanza complicato. La classe org.jdom.output.XMLOutputter si occupa appunto di questo ed ha un pratico metodo output() che accetta come argomenti un documento JDOM e un java.io.OutputStream. Ad esempio per serializzare un documento su standard output bastano le seguenti due righe di codice

XMLOutputter outputter = new XMLOutputter();
outputter.output(doc, System.out);

Per ottenere una stringa si usa invece il metodo outputString():

XMLOutputter outputter = new XMLOutputter();
String str = outputter.outputString(doc);

La classe SAXOutputter permette di generare uno stream di eventi SAX:

ContentHandler handler = ...;
SAXOutputter outputter = new SAXOutputter(handler);
outputter.output(doc);

Per ottenere un org.w3c.dom.Document a partire da un documento JDOM si usa invece l'apposita classe DOMOutputter:

org.jdom.Document jdoc = ...;
org.w3c.dom.Document doc;

DOMOutputter outputter = new DOMOutputter();
doc = outputter.output(jdoc);

Costruzione di un documento JDOM
Facendo riferimento al seguente documento XML

<todolist>
<item>
<number>1</number>
<priority>6</priority>
<description>Leggere la posta</description>
<state>2</state>
</item>
<item>
<number>2</number>
<priority>9</priority>
<description>Riunione</description>
<state>2</state>
</item>
<item>
<number>3</number>
<priority>8</priority>
<description>Andare a correre nel parco</description>
<state>1</state>
</item>
</todolist>

ecco un esempio di costruzione di un documento JDOM in maniera 'manuale', ovvero senza eseguire il parsing di una qualche sorgente dati, e sua serializzazione su standard output:

package com.mokabyte.javaxml.jdom;

import java.io.*;
import org.jdom.*;
import org.jdom.output.*;

public class JDOMCreation {

public static void main(String[] args) {
Element root = new Element("todolist"), item;
Document doc = new Document(root);

item = buildItem("1", "6", "Leggere la posta", "2");
root.addContent(item);

item = buildItem("2", "9", "Riunione", "2");
root.addContent(item);

item = buildItem("3", "8", "Andare a correre nel parco", "1");
root.addContent(item);

try {
// serializzazione su standard output
XMLOutputter outputter = new XMLOutputter();
outputter.setNewlines(true);
outputter.setIndent(" ");
outputter.output(doc, System.out);
} catch(IOException e) {
System.err.println("Errore durante la serializzazione del documento");
e.printStackTrace();
}
}

private final static Element buildItem(String number,
String priority,
String description,
String state) {
Element item = new Element("item"),
numberEl = new Element("number"),
priorityEl = new Element("priority"),
descriptionEl = new Element("description"),
stateEl = new Element("state");

numberEl.setText(number);
priorityEl.setText(priority);
descriptionEl.setText(description);
stateEl.setText(state);

item.addContent(numberEl);
item.addContent(priorityEl);
item.addContent(descriptionEl);
item.addContent(stateEl);

return item;
}
}

Il metodo buildItem() costruisce un elemendo JDOM corrispondente ad un'attività della todo-list:

  • si crea un elemento item;
  • si creano i vari elementi corrispondenti ai dati di un'attività (numero, priorità, descrizione e stato);
  • si impostano i valori mediante il metodo setText() di Element;
  • si 'appendono' gli elementi contenenti i dati all'elemento item, ritornato poi come risultato.

Si noti come a differenza di DOM il valore di un elemento non venga gestito come un nodo figlio da appendere ma in maniera molto più pratica mediante un apposito metodo, come fosse una sua proprietà.

 

Accesso agli elementi di un documento
Il modello a oggetti di JDOM permette di navigare la struttura di un documento in maniera molto semplice; ad esempio dato un Document è possibile accedere all'elemento radice con:

Element root = doc.getRootElement();

A questo punto si può accedere alla lista di tutti i nodi figli

List childrenList = root.getChildren();

o a tutti i nodi figli aventi lo stesso nome

List childrenList = root.getChildren("CHAPTER");

Le precedenti linee di codice sono molto semplice e allo stesso tempo dimostrano quanto JDOM sia un tool vicino allo sviluppatore Java: il metodo getChildren() ritorna un oggetto java.util.List che lo sviluppatore può utilizzare con gli idiomi a cui è abituato. Le collezioni ritornate dai metodi di JDOM sono inoltre aggiornabili e le modifiche si ripercuotono sul Document corrispondente. Ad esempio con

childrenList.add(new Element("PARAGRAPH"));

si aggiunge un nodo discendente all'elemento CHAPTER.
Per accedere ad un singolo nodo figlio si usa il metodo getChild(); in questo caso si accede al primo nodo CHAPTER partendo dalla radice del documento

Element e = root.getChild("CHAPTER");

Per accedere al contenuto di un elemento si usa il metodo getContent()

String content = e.getContent();

Utilizzando DOM si sarebbe invece dovuto scrivere

String content = e.getFirstChild().getNodeValue();

Il seguente esempio mostra, una volta letto il documento XML visto in precedenza e creato il corrispondente documento JDOM, come accedere agli elementi in esso contenuti:

package com.mokabyte.javaxml.jdom;

import org.jdom.*;
import org.jdom.input.SAXBuilder;
import java.io.File;
import java.util.*;

public class JDOMTest {
public static void main(String[] args) {

if (args.length != 1) {
System.out.println("Utilizzo: java com.mokabyte.javaxml.jdom.JDOMTest <file>");
return;
}


try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new File(args[0]));

List children = doc.getRootElement().getChildren();
String number, priority, description, state;
Iterator iterator = children.iterator();

while(iterator.hasNext()) {
Element item = (Element)iterator.next();

number = item.getChildText("number");
priority = item.getChildText("priority");
description = item.getChildText("description");
state = item.getChildText("state");

System.out.println("Attivita' " + number);
System.out.println("\tPriorita': " + priority);
System.out.println("\tDescrizione: " + description);
System.out.println("\tStato: " + state);
}

} catch(JDOMException e) {
System.err.println("Errore durante il parsing del documento " + args[0]);
e.printStackTrace();
}
}
}

L'output del programma precedente è

Attivita' 1
Priorita': 6
Descrizione: Leggere la posta
Stato: 2
Attivita' 2
Priorita': 9
Descrizione: Riunione
Stato: 2
Attivita' 3
Priorita': 8
Descrizione: Andare a correre nel parco
Stato: 1

Attributi
Nel caso in cui l'elemento abbia un attributo lo si può ottenere come stringa

String attrValue = e.getAttributeValue("name");

o come istanza della classe Attribute

Attribute attribute = e.getAttribute("name");
String value = attribute.getValue();

E' inoltre possibile accedere al valore tipizzato di un attributo mediante una serie di metodi specifici. Ad esempio per ottenere il valore di un attributo numerico è possibile scrivere

int length = attribute.getAttribute("length").getIntValue();

Per impostare il valore di un attributo si scriverà invece

e.setAttribute("height", "20");

Conclusioni
JDOM è uno strumento veramente interessante per lo sviluppatore Java:

  • il suo modello a oggetti è molto più pratico di DOM. Ad esempio il poter creare gli oggetti direttamente con il costruttore è sicuramente più immediato che usare metodi factory;
  • l'utilizzo delle collection semplifica inoltre notevolmente il codice necessario ed è un idioma di programmazione molto vicino al programmatore Java.
    Il futuro di JDOM è molto promettente anche perchè il progetto è stato proposto come Java Specification Request e potrebbe essere incluso in una delle prossime versioni di Java.

Bibliografia
[1] JDOM - http://www.jdom.org
[2] MokaByte - "Manuale pratico di Java", HOPS Libri
[3] Elliotte Rusty Harold - "Processing XML with Java", http://www.cafeconleche.org/books/xmljava/chapters/index.html
[4] Wes Biggs, Harry Evans - "Simplify XML Programming with JDOM", IBM developerWorks

 

Risorse
Scarica qui i sorgenti presentati nell'articolo

 

Andrea Giovannini si occupa di architetture per applicazioni enterprise Web-based con tecnologia J2EE, con particolare interesse per XML.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it