Java JAXB
Mappa XML su oggetti Java e viceversa con annotazioni JAXB e Marshaller/Unmarshaller.
JAXB (Jakarta XML Binding, precedentemente Java Architecture for XML Binding) mappa gli oggetti Java su XML e viceversa senza dover scrivere codice di parsing a mano. Annoti una classe normale, poi la passi a un Marshaller per produrre XML o a un Unmarshaller per leggere XML in oggetti. JAXB era incluso nel JDK (javax.xml.bind) fino a Java 8, è stato rimosso in Java 11 e ora viene distribuito come dipendenza separata nel namespace jakarta.xml.bind. Le annotazioni e il modello marshal/unmarshal sono gli stessi in entrambi.
Questo capitolo tratta cos'è il binding JAXB, le annotazioni principali, come marshallizzare un oggetto in XML e de-marshallizzare XML in oggetti, come vengono mappate le collezioni e la differenza di namespace tra Java 8 e le versioni moderne. JAXB è un'API di binding: a differenza dei parser di basso livello DOM e SAX trattati in precedenza in questa parte, non tocchi mai l'albero XML — lavori con normali oggetti Java.
Quando usare JAXB
Usa JAXB quando i tuoi dati hanno già (o meritano) una classe e XML è solo il formato di trasporto o storage:
- Lettura e scrittura di file di configurazione o documenti dove la struttura è stabile e nota in anticipo.
- Servizi web SOAP / legacy, dove il contratto è uno schema XML e gli strumenti generano le classi.
- Round-trip — caricare XML, modificare l'oggetto e riscriverlo senza parsing manuale.
Usa invece DOM o SAX quando la struttura è irregolare, hai bisogno solo di pochi campi da un documento di grandi dimensioni, o non esiste una classe naturale a cui fare binding. E se controlli entrambe le estremità e hai solo bisogno di un formato dati compatto, JSON con Jackson è di solito più leggero di XML.
L'idea fondamentale: le annotazioni descrivono il mapping
Non scrivi codice che percorre l'albero XML. Invece descrivi, con annotazioni su una classe, come i suoi campi corrispondono agli elementi e agli attributi XML. JAXB legge quelle annotazioni a runtime e genera la conversione per te in entrambe le direzioni. Un POJO diventa uno schema auto-documentante.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlAttribute;
@XmlRootElement(name = "book")
public class Book {
private String title;
private String author;
private int year;
@XmlElement public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
@XmlElement public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
@XmlAttribute public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
// JAXB requires a public no-arg constructor for unmarshalling
public Book() {}
}Le annotazioni principali
Un insieme di annotazioni copre quasi ogni tipo di mapping. Si trovano nel package jakarta.xml.bind.annotation (o javax.xml.bind.annotation su Java 8).
| Annotazione | Effetto |
|---|---|
@XmlRootElement | Contrassegna una classe come radice del documento; nomina l'elemento più esterno |
@XmlElement | Mappa un campo/proprietà a un elemento annidato |
@XmlAttribute | Mappa un campo/proprietà a un attributo sull'elemento |
@XmlElementWrapper | Racchiude una collezione in un elemento contenitore |
@XmlTransient | Esclude un campo dall'XML completamente |
@XmlAccessorType | Controlla se JAXB fa binding dei campi o dei getter per default |
Marshalling: da oggetto a XML
Un JAXBContext è il punto di ingresso — creane uno per le tue classi radice, poi chiedile un Marshaller. Il marshaller trasforma un grafo di oggetti in XML. Impostare JAXB_FORMATTED_OUTPUT produce un output leggibile e indentato.
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
Book book = new Book();
book.setTitle("Effective Java");
book.setAuthor("Joshua Bloch");
book.setYear(2018);
JAXBContext context = JAXBContext.newInstance(Book.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(book, System.out);
// <book year="2018"><title>Effective Java</title><author>Joshua Bloch</author></book>Unmarshalling: da XML a oggetto
L'operazione inversa è simmetrica: chiedi allo stesso JAXBContext un Unmarshaller e puntalo su una sorgente — un File, InputStream, Reader, o StringReader. JAXB costruisce l'oggetto usando il costruttore senza argomenti e lo popola dagli elementi e attributi.
import jakarta.xml.bind.Unmarshaller;
import java.io.StringReader;
String xml = "<book year=\"2018\">"
+ "<title>Effective Java</title>"
+ "<author>Joshua Bloch</author></book>";
Unmarshaller unmarshaller = context.createUnmarshaller();
Book book = (Book) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(book.getTitle()); // Effective Java
System.out.println(book.getYear()); // 2018JAXB non è nel classpath di questo code runner (è una dipendenza esterna su Java moderno), quindi l'esempio pratico seguente dimostra lo stesso round trip marshal/unmarshal usando solo l'API DOM integrata nel JDK. Il concetto è identico: un attributo sull'elemento radice, elementi figli per i campi e un round trip che restituisce un oggetto uguale.
Cosa osservare dall'esecuzione:
- L'XML marshallizzato mette
yearcome attributo su<book>matitleeauthorcome elementi figli — esattamente la distinzione che@XmlAttributerispetto a@XmlElementcontrolla nel vero JAXB. La scelta dell'annotazione determina elemento-vs-attributo. - Il tag radice è
book, riportato dael.getTagName(). In JAXB quel nome viene da@XmlRootElement(name = "book"); qui è la stringa passata acreateElement. In entrambi i casi, l'elemento più esterno identifica il tipo del documento. - Il marshalling e l'unmarshalling sono operazioni speculari sulla stessa struttura: il programma costruisce XML da un
Book, poi ricostruisce unBookda quell'XML. IlMarshallere l'Unmarshallerdi JAXB sono esattamente questa coppia supportata da un unicoJAXBContext. round-trip equal : truedimostra che i dati hanno superato il percorso intatti — title, author e year sono tutti tornati. Un binding corretto è privo di perdita, che è la proprietà su cui fai affidamento quando XML è il tuo formato wire.- Leggere
yearindietro ha richiestoInteger.parseIntperché XML è tutto testo. JAXB nasconde questo convertendo automaticamente il testo di attributi ed elementi nel tipo Java dichiarato (int,LocalDate,BigDecimal); senza di esso, ogni campo è una stringa che devi analizzare tu stesso.
Mappare le collezioni
Una List si mappa a elementi ripetuti. Per default ogni elemento viene nominato secondo il campo, il che può produrre un documento piatto e difficile da leggere. @XmlElementWrapper aggiunge un elemento contenitore in modo che gli elementi siano raggruppati — il pattern comune e leggibile.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
@XmlRootElement(name = "library")
public class Library {
private List<Book> books;
@XmlElementWrapper(name = "books") // outer <books> element
@XmlElement(name = "book") // each item is a <book>
public List<Book> getBooks() { return books; }
public void setBooks(List<Book> books) { this.books = books; }
public Library() {}
}Con il wrapper, l'output si annida in modo ordinato:
<library>
<books>
<book year="2018"><title>Effective Java</title>...</book>
<book year="2008"><title>Clean Code</title>...</book>
</books>
</library>Rimuovi @XmlElementWrapper e gli elementi <book> si trovano direttamente sotto <library> senza elemento di raggruppamento — valido, ma più piatto. Scegliere tra i due è la decisione di mapping delle collezioni più comune in JAXB.
Java 8 vs. Java moderno: il cambio di namespace
Il problema più grande è il cambio di nome del package. Su Java 8 l'API è inclusa e si trova sotto javax.xml.bind. Da Java 11 in poi è separata e si trova sotto jakarta.xml.bind, aggiunta come dipendenza.
| Java 8 | Java 11+ | |
|---|---|---|
| Package | javax.xml.bind | jakarta.xml.bind |
| Nel classpath? | Integrato | Aggiungi una dipendenza |
| Artifact runtime | JDK | org.glassfish.jaxb:jaxb-runtime |
Per una build Maven su Java moderno, aggiungi l'API più un'implementazione runtime:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>Pratica
Vedi anche
- Lavorare con XML in Java — panoramica delle API XML e quando ognuna si adatta.
- Parser DOM e parser SAX — le alternative di basso livello al binding.
- Jackson — la stessa idea di annotare-e-fare-binding applicata a JSON.