JAXB marshalling: da oggetto Java a XML

In questo post vediamo un esempio di serializzazione XML, chiamata in questo contesto marshalling, di un oggetto Java tramite JAXB, Java Architecture for XML Binding, ovvero le APIs messe a disposizione dalla piattaforma Java, sia Standard che Enterprise, per mappare uno schema XML in un oggetto Java e viceversa.

Per il nostro scopo, ho utilizzato un semplice progetto Spring Boot creato con il suo IDE Spring Tool Suite, ma in realtà si tratta di un normalissimo progetto Java senza requisiti particolari.

Dopo aver creato il progetto, creiamo un package chiamato “model” in cui andiamo a creare la classe che rappresenta la nostra entità di cui andremo a convertire in XML le istanze.
Creiamo ad esempio la classe “User”, con le seguenti proprietà:

  • id
  • firstName
  • lastName
  • website
  • email

Il codice della classe User sarà quindi:

public class User {

	private long id;
	private String firstName;
	private String lastName;
	private String website;
	private String email;

	// GETTERS e SETTERS omessi per semplicità
}

JAXB classe model User

A questo punto dobbiamo iniziare ad indicare quale dovrà essere la struttura del nostro XML risultante e quali proprietà della nostra classe ci vogliamo inserire.
Per farlo dobbiamo definire:

  • La root del nostro documento XML
  • Gli elementi da inserire nell’XML
  • Gli attributi da inserire all’interno degli elementi XML

Per soddisfare queste richieste JAXB ci mette a disposizione tre annotazioni, che sono rispettivamente:

  • @XmlRootElement
  • @XmlElement
  • @XmlAttribute

La prima va applicata a livello di classe

@XmlRootElement
public class User {

}

mentre le altre due vanno applicate a livello di proprietà della classe, specificando che è ad esse che vogliamo accedere per creare i binding tramite l’annotazione @XmlAccessorType(XmlAccessType.FIELD), altrimenti JAXB utilizza come default XmlAccessType.PUBLIC_MEMBER che genera i binding sia per le proprietà annotate che per quelle che hanno un getter pubblico.

@XmlRootElement()
@XmlAccessorType(XmlAccessType.FIELD)
public class User {

	@XmlElement
	private long id;
	@XmlElement
	private String firstName;
	@XmlElement
	private String lastName;
	@XmlElement
	private String website;
	@XmlElement
	private String email;

	// GETTERS e SETTERS omessi per semplicità
}

Esempio classe Java annotata con JAXB
A questo punto dobbiamo creare una semplice applicazione di prova per verificare che la serializzazione XML di un’istanza della nostra classe funzioni correttamente. Essendo partiti da un’applicazione SpringBoot, andiamo nella classe della nostra applicazione, in questo caso “JaxbExample2Application.java”, e commentiamo la prima riga, quella in cui appunto viene fatta partire l’applicazione. Questo perchè in realtà non ci serve far partire Spring Boot con tutto il suo stack, ma solo sfruttare il main per istanziare un oggetto e stampare in output la sua serializzazione XML.

Gli step da eseguire per ottenere quanto appena detto sono:

  • Istanziare un oggetto della classe User
  • Creare un contesto JAXB (JAXBContext)
  • Creare un Marshaller
  • Invocare il metodo marshal del Marshaller, passandogli come parametro l’oggetto User e lo stream di output

Per soddisfare il primo punto, creiamo per semplicità nella classe User il costruttore full-args

@XmlRootElement()
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
	
	@XmlElement
	private long id;
	@XmlElement
	private String firstName;
	@XmlElement
	private String lastName;
	@XmlElement
	private String website;
	@XmlElement
	private String email;
	
	public User(long id, String firstName, String lastName, String website, String email) {
		super();
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.website = website;
		this.email = email;
	}
	
	// GETTERS e SETTERS omessi per semplicità
}

e poi nella classe della nostra applicazione lo utilizziamo per creare un’istanza della classe:

User u = new User(0, "Davis", "Molinari", "www.davismol.net", "myemailaddress@gmail.com");

A questo punto possiamo creare il contesto JAXB

JAXBContext jc = JAXBContext.newInstance(User.class);

e il Marshaller, settandogli anche la proprietà per formattare in maniera leggibile l’output della serializzazione XML

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

Infine, possiamo invocare il metodo marshal() dell’oggetto Marshaller, passandogli l’oggetto della classe User o lo stream su cui vogliamo scrivere l’XML, che in questo caso sarà la console, quindi System.out

marshaller.marshal(u, System.out);

Il tutto potrebbe generare qualche eccezione, quindi la sequenza va racchiusa in un blocco try-multicatch. Il codice completo dell’applicazione per testare il marshalling diventa quindi:

public class JaxbExample2Application {

	public static void main(String[] args) {
		//SpringApplication.run(JaxbExample2Application.class, args);
		User u = new User(0, "Davis", "Molinari", "www.davismol.net", "myemailaddress@gmail.com");
		
		try {
			JAXBContext jc = JAXBContext.newInstance(User.class);

			Marshaller marshaller = jc.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.marshal(u, System.out);
			
		} catch (PropertyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Proviamo ad eseguire la nostra applicazione e…

JAXB Java Errore assenza costruttore zero argomenti
Abbiamo ottenuto un errore!

Alla nostra classe dobbiamo aggiungere anche il costruttore di zero argomenti altrimenti, eseguendo un tentativo di serializzazione XML di una sua istanza, otteniamo l’errore seguente:

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
net.davismol.model.User non dispone di un costruttore senza argomenti predefinito.
	this problem is related to the following location:
		at net.davismol.model.User

Aggiungiamo quindi il costruttore vuoto

@XmlRootElement()
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
	
	@XmlElement
	private long id;
	@XmlElement
	private String firstName;
	@XmlElement
	private String lastName;
	@XmlElement
	private String website;
	@XmlElement
	private String email;
	
	public User() {
	    super();
	}
	
	// GETTERS e SETTERS omessi per semplicità
}

e proviamo ad eseguire nuovamente l’applicazione. Questa volta l’esecuzione va a buon fine e il risultato mostrato in output è il seguente



    0
    Davis
    Molinari
    www.davismol.net
    myemailaddress@gmail.com

JAXB marshalling XML di un oggetto Java
Finora abbiamo visto soltanto l’utilizzo dell’annotazione @XmlElement e abbiamo tralasciato @XmlAttribute. Proviamo ora invece ad utilizzarla, per definire che una determinata proprietà non deve risultare nell’XML generato come un elemento ma piuttosto come un attributo del nodo root, “user” in questo caso.
Prendiamo ad esempio la proprietà “email” della classe User e annotiamola con @XmlAttribute invece che con @XmlElement

@XmlRootElement()
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
	
	@XmlElement
	private long id;
	@XmlElement
	private String firstName;
	@XmlElement
	private String lastName;
	@XmlElement
	private String website;
	@XmlAttribute
	private String email;

	public User() {
		super();
	}

	// GETTERS e SETTERS omessi per semplicità
}

Se proviamo ad eseguire nuovamente l’applicazione otteniamo il risultato seguente:



    0
    Davis
    Molinari
    www.davismol.net

in cui vediamo che è sparito dall’XML il tag email ed è comparso invece il suo valore come attributo dell’elemento “user”.

JAXB marshalling Java con XMLAttribute

L’intero progetto di esempio è scaricabile qui:

Leggi anche:

This entry was posted in $1$s. Bookmark the permalink.

One thought on “JAXB marshalling: da oggetto Java a XML

  1. Pingback: JAXB: aggiungere namespace e schema location nel marshalling XML di un oggetto Java | Dede Blog

Leave a Reply

Your email address will not be published. Required fields are marked *