In questo articolo daremo una occhiata ai concetti principali di Gson, una libreria sviluppata da Google, che rende incredibilmente semplice e veloce la conversione tra gli oggetti Java e la loro rappresentazione JSON.
Originariamente la libreria fu creata per essere utilizzata esclusivamente per l'uso interno nei progetti Google, in seguito è stata resa disponibile pubblicamente per gli sviluppatori, e può quindi essere utilizzata nelle nostre applicazioni, in alternativa ad altre librerie analoghe (come ad esempio Jettison o Jabsorb, ad esempio.
Sul sito ufficiale del progetto, è possibile scaricare l’ultima versione rilasciata (attualmente la 2.3).
Con l'affermazione del Web 2.0, è ormai una esigenza comune quella di serializzare e deserializzare gli oggetti in formato JSON nella maniera più semplice e standard possibile: pensiamo ad esempio alle risorse consumate su servizi REST, così come a formati di scambio per applicazioni mashup.
Le caratteristiche principali di GSON sono in particolare le seguenti:
- Semplice e veloce da utilizzare.
- Indipendente dai file sorgenti. Quasi tutte le librerie analoghe, invece, richiedono l'inserimento di alcune annotazioni nei bean che occorre serializzare. Questa procedura è improponibile se non si ha a disposizione il codice sorgente, personalizzabile.
- È possibile creare degli adapter personalizzati per definire la modalità di serializzazione degli oggetti.
- Ampio supporto di Java Generics.
Un esempio pratico
Per i nostri esempi, creiamo un semplice bean User
, con alcune proprietà.
package it.lucasantaniello.tutorial.gson;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String name;
private String surname;
private Date birtday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Date getBirtday() {
return birtday;
}
public void setBirtday(Date birtday) {
this.birtday = birtday;
}
@Override
public String toString() {
return String.format("User [id=%s, name=%s, surname=%s, birtday=%s]", id, name, surname, birtday);
}
}
serializzazione
Serializzare il nostro oggetto è semplicissimo. È necessario creare un'istanza della classe com.google.gson.Gson
ed invocare il metodo toJson()
che riceve in ingresso un Object
.
Nel primo caso passiamo un oggetto User
, nel secondo caso una lista di oggetti User
. In entrambi i casi GSON restituisce una stringa che rappresenta la risorsa in esame: nel primo caso un singolo oggetto User
in formato JSON, nel secondo caso, una lista di oggetti User
in formato JSON.
User user = new User();
user.setId(1);
user.setName("Luca");
user.setSurname("Santaniello");
user.setBirtday(getDate("06-04-1981", "dd-MM-yyyy"));
User user2 = new User();
user2.setId(2);
user2.setName("Pocho");
user2.setSurname("Lavezzi");
user2.setBirtday(getDate("10-10-1986", "dd-MM-yyyy"));
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
Gson gson = new Gson();
String userJson = gson.toJson(user);
System.out.println(userJson);
String usersJson = gson.toJson(users);
System.out.println(usersJson);
Ed ecco il risultato del nostro primo test.
userJson = {
"id":1,
"name":"Luca",
"surname":"Santaniello",
"birtday":"Apr 6, 1981 12:00:00 AM"
}
usersJson = [
{
"id":1,
"name":"Luca",
"surname":"Santaniello",
"birtday":"Apr 6, 1981 12:00:00 AM"
}, {
"id":2,
"name":"Pocho",
"surname":"Lavezzi",
"birtday":"Oct 10, 1986 12:00:00 AM"
}
]
deserializazione
Adesso ipotizziamo di avere la nostra stringa JSON e dover deserializzarla in un oggetto.
Anche in questo caso GSON mette a disposizione un unico metodo da invocare fromJson()
, che riceve in ingresso la stringa json e la classe nella quale deserializzarla.
Nel caso in cui sia necessario deserializzare una Lista occorre dichiarare il tipo di oggetti contenuti nella lista utilizzando la classe com.google.gson.reflect.TypeToken
.
Come è possibile notare, non è possibile deserializzare liste che contengono oggetti di natura differente.
String jsonUser = "{"id":1,"name":"Luca","surname":"Santaniello", "birthday":"Apr 6, 1981 12:00:00 AM"}";
Gson gson = new Gson();
User user = gson.fromJson(jsonUser, User.class);
System.out.println(user);
String jsonUsers = "[{"id":1,"name":"Luca","surname":"Santaniello"},{"id":2,"name":"Pocho","surname":"Lavezzi"}]";
Type collectionType = new TypeToken<Collection<User>(){}.getType();
Collection<User> users = gson.fromJson(jsonUsers, collectionType);
for (User u : users) {
System.out.println(u);
}
Potrebbe sorgere l’esigenza di voler definire un formato particolare per la data, ad esempio dd/MM/yyyy
.
Ipotizziamo che la nostra stringa json sia la seguente:
{"id":1,"name":"Luca","surname":"Santaniello", "birtday":"06-04-1981"}
Se lanciamo nuovamente il test otteniamo un'eccezione di tipo ParseException
. Di default infatti, GSON utilizza il formato standard delle date (ad esempio: Apr 6, 1981 10:20:00 AM
):
ParseException Caused by: java.text.ParseException: Unparseable date: "06-04-1981" at java.text.DateFormat.parse(DateFormat.java:337) at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:79) ... 10 more
Per risolvere questo genere di problemi, Gson ci mette a disposizione la classe GsonBuilder
, mediante la quale è possibile definire le impostazioni del serializzatore ed impostare alcune proprietà, tra le quali il formato della data.
String jsonUser = "{"id":1,"name":"Luca","surname":"Santaniello", "birtday":"06-04-1981"}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("dd-MM-yyyy");
Gson gson = gsonBuilder.create();
User user = gson.fromJson(jsonUser, User.class);
System.out.println(user);
GsonBuilder
naturalmente può essere utilizzato sia in fase di serializzazione che in fase di deserializzazione.
adapter personalizzati
È possibile, inoltre, definire degli Adapter
personalizzati. Per definire il nostro custom adapter è necessario creare una classe che implementa JsonSerializer e JsonDeserializer per serializzare e deserializzare l'oggetto.
Ipotizziamo di voler stabilire la modalità di serializzazione e deserializzazione di tutti gli oggetti GregorianCalendar
. Di default Gson serializza un GregorianCalendar
con una stringa di questo tipo:
"birtdayCalendar":{
"year":1981,
"month":3,
"dayOfMonth":6,
"hourOfDay":0,
"minute":0,
"second":0
}
e ipotizziamo di volerlo stampare invece in questo modo:
"birtdayCalendar":{“06-04-1981”}
Creiamo il nostro adapter ed implementiamo la logica dei metodi serialize e deserialize.
package it.lucasantaniello.tutorial.gson;
public class CalendarAdapter
implements JsonSerializer<Calendar>,
JsonDeserializer<Calendar> {
private String dateFormat;
public CalendarAdapter(String dateFormat) {
this.dateFormat = dateFormat;
}
@Override
public JsonElement serialize(Calendar calendar,
Type type, JsonSerializationContext context) {
String str = null;
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
if (calendar != null) {
str = sdf.format(calendar.getTime());
}
return new JsonPrimitive(str);
}
@Override
public Calendar deserialize(JsonElement json,
Type type, JsonDeserializationContext context)
throws JsonParseException {
String strDate = json.getAsString();
Calendar calendar = null;
try {
calendar = Calendar.getInstance();
DateFormat formatter = new SimpleDateFormat(dateFormat);
Date date = (Date) formatter.parse(strDate);
calendar.setTimeInMillis(date.getTime());
} catch (ParseException e) {
throw new JsonParseException("calendar parsing error");
}
return calendar;
}
}
Per attivare il nostro adapter è necessario registrarlo utilizzando il GsonBuilder
:
gsonBuilder.registerTypeAdapter(
GregorianCalendar.class,
new CalendarAdapter("dd-MM-yyyy")
);