JAXB: aggiungere namespace e schema location nel marshalling XML di un oggetto Java

In un post precedente abbiamo visto come effettuare il marshalling XML di un oggetto Java con JAXB, le APIs di Java per il binding tra XML e oggetti e viceversa. In questo nuovo articolo vediamo come inserire nella generazione dell’XML e, più precisamente del nodo root, anche le informazioni relative al namespace e alla location dello schema xsd di riferimento.

Per chiarire meglio di cosa si tratta, prendiamo come esempio un file POM di Maven, la cui struttura contiene un nodo root chiamato “project” definito in questo modo:





La stringa

xmlns="http://maven.apache.org/POM/4.0.0"

definisce il namespace di appartenenza degli elementi, che altro non è che un identificativo univoco utilizzato per riferirsi ai nodi corretti in caso di presenza di elementi appartenenti a più namespaces. Solitamente è indicato come un URL, ma non deve esserlo necessariamente ne, tantomeno, deve essere un URL raggiungibile. Nel caso ci sia un solo namespace all’interno del documento, il suo prefisso viene omesso davanti al nome degli elementi.

Le stringhe

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"

invece definiscono un ulteriore namespace, a cui si riferirà con la stringa “xsi” che sta per XML Schema Instance, e valorizzano l’attributo “schemaLocation” del namespace xsi con l’indirizzo dello schema xsd che definisce gli elementi del namespace indicato (http://maven.apache.org/POM/4.0.0). L’URL dello schema xsd in questo caso deve invece puntare ad un file xsd vero e proprio. (infatti l’url dello schema xsd del namespace dei file Maven è raggiungibile)

Dopo questo brevissimo riepilogo sul significato di namespace e location dello schema, vediamo come aggiungerli nel marshalling XML di un oggetto Java.

I meccanismi per generare le due informazioni sono differenti:

  • Per generare il namespace si una un’annotazione a livello di package
  • Per generare lo schemaLocation si setta una proprietà dell’oggetto Marshaller

Vedremo ora come generare queste due informazioni, partendo dall’esempio del post precedente e modificando opportunamente le classi del progetto. Avevamo quindi la classe “User” che era il nostro model di cui volevamo eseguire il marshalling XML e una applicazione di test che effettuava l’operazione.

La classe User si presentava cosi:

@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à 
}

Mentre la classe dell’applicazione conteneva il seguente codice:

@SpringBootApplication
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();
		}
	}
}

Partiamo quindi con le modifiche per ottenere la generazione del namespace.
Per creare un’annotazione a livello di package dobbiamo creare, all’interno del package stesso, un file package-info.java
In Eclipse e in Spring Tool Suite per farlo basta semplicemente cliccare sul package in questione, “model” nel nostro caso, con il tasto destro e dal menù contestuale scegliere: “New” -> “Package”

Eclipse STS Create new package
A questo punto si apre il form per la creazione del nuovo package ma, in realtà, noi non ne creeremo uno nuovo ma usiamo questo metodo per editare quello esistente, lasciando il nome intatto ma flaggando la checkbox “Create package-info.java

Eclipse STS Flag create package-info
Ora che abbiamo creato il file “package-info.java” all’interno del nostro package “model”, inseriamo l’annotazione necessaria. Si tratta dell’annotazione @XmlSchema di JAXB che utilizziamo in questo modo all’inizio del file, prima dello statement “package”:

@XmlSchema(
	namespace = "http://www.mynamespaceuniqueid.com",
	elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
)

package net.davismol.model;

import javax.xml.bind.annotation.XmlSchema;

JAXB XMLSchema annotation example
Proviamo quindi ad eseguire la nostra applicazione dopo queste modifiche. Il risultato che otteniamo è il seguente XML, come vediamo anche dalla figura sottostante:



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

JAXB XML namespace marshalling

Come possiamo vedere, nell’elemento “user” che rappresenta la root del nostro XML, è stata aggiunta l’indicazione del namespace

xmlns="http://www.mynamespaceuniqueid.com"

La seconda informazione che vogliamo aggiungere alla root del nostro XML è la schemaLocation. Come abbiamo detto in precedenza, in questo caso non dobbiamo utilizzare un’annotazione ma ci basta settare una proprietà dell’oggetto Marshaller che usiamo per serializzare in XML l’oggetto della nostra classe. In particolare la proprietà che dobbiamo settare è JAXB_SCHEMA_LOCATION e lo facciamo in questo modo:

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
		"http://www.mynamespaceuniqueid.com http://www.mynamespaceuniqueid.com/xsd/myschema.xsd");

La proprietà prende come valore una coppia di stringhe che rappresentano il namespace e l’indirizzo del relativo schema. Il namespace è lo stesso dichiarato poco fa, mentre in questo caso utilizziamo per lo schema un URL di esempio al quale non si trova il file.
Il codice completo dell’applicazione di esempio 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.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
					"http://www.mynamespaceuniqueid.com http://www.mynamespaceuniqueid.com/xsd/myschema.xsd");
			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();
		}
	}
}

ed eseguendolo otteniamo il risultato seguente:



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

Abbiamo quindi correttamente aggiunto al nostro XML di output anche l’informazione sulla schemaLocation.
JAXB XML namespace and location marshal

Anche in questo caso, il codice completo del progetto di esempio è scaricabile qui:

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

One thought on “JAXB: aggiungere namespace e schema location nel marshalling XML di un oggetto Java

  1. Ciao,
    ottimo, mi hai chiarito un mucchio di cose a proposito del file package-info.java
    Il mio problema è che nel root del body, oltre al ns del package-info, devo inserire altri due ns con questo formato
    xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

    Ho provato con
    marshallerObj.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, “http://www.w3.org/2001/XMLSchema-instance”);
    ma come output mi ritrovo
    xsi:schemaLocation=”http://www.w3.org/2001/XMLSchema-instance”
    (senza “xmlns:” e con “schemaLocation” in più).

    Come posso fare?

    Grazie!

Leave a Reply to Francesco Cancel reply

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