1. Introduzione a Gson
Ci siamo già familiarizzati con Jackson e abbiamo visto perché è considerato lo standard de facto per lavorare con JSON in Java. Ma esiste un’altra libreria che ha conquistato enorme popolarità, soprattutto nel mondo Android — è Gson. Gson è stata creata in Google come soluzione leggera e semplice per serializzare e deserializzare oggetti Java in JSON. È apprezzata per la sua bassa soglia d’ingresso: per iniziare quasi non serve alcuna configurazione — la maggior parte dei compiti si risolve letteralmente «pronta all’uso».
Un altro vantaggio di Gson è la leggerezza. La libreria occupa poco spazio e non porta con sé molte dipendenze, quindi è spesso usata dove conta la dimensione dell’applicazione, ad esempio sui dispositivi mobili. Gson è diventato lo standard di fatto per i progetti Android — compattezza e semplicità giocano un ruolo decisivo.
A proposito, il nome Gson significa Google JSON. A volte nella community si trova l’espansione scherzosa — Genius’ Son («figlio del genio»), ma naturalmente non è ufficiale: è solo un gioco di parole.
Aggiungere Gson al progetto
Se usi Maven o Gradle, aggiungi semplicemente la dipendenza (la versione può variare):
Maven:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
Gradle:
implementation 'com.google.code.gson:gson:2.10.1'
Non abbiamo ancora studiato i sistemi di build, quindi per iniziare puoi semplicemente scaricare il file jar dalla pagina ufficiale di Gson e aggiungerlo al progetto.
2. Operazioni di base: serializzazione e deserializzazione
Vediamo come serializzare e deserializzare oggetti con Gson usando un semplice esempio di classe.
Esempio: classe User
// Classe di esempio
public class User {
private String name;
private int age;
private boolean active;
// Costruttore
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// Getter e setter (Gson li usa se necessario)
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
Serializzazione: oggetto → JSON
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
User user = new User("Alice", 25, true);
Gson gson = new Gson();
String json = gson.toJson(user);
System.out.println(json);
// {"name":"Alice","age":25,"active":true}
}
}
Nota: i campi vengono serializzati con i nomi definiti nella classe!
Deserializzazione: JSON → oggetto
public class GsonExample {
public static void main(String[] args) {
String json = "{\"name\":\"Bob\",\"age\":30,\"active\":false}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
System.out.println(user.getName()); // Bob
System.out.println(user.getAge()); // 30
System.out.println(user.isActive());// false
}
}
Lavorare con liste di oggetti
Gson è un po’ più macchinoso con le collezioni rispetto a Jackson, ma è tutto risolvibile.
import java.util.List;
import java.util.Arrays;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
public class GsonListExample {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 30, false)
);
Gson gson = new Gson();
String json = gson.toJson(users);
System.out.println(json);
// [{"name":"Alice","age":25,"active":true},{"name":"Bob","age":30,"active":false}]
// Deserializzazione della lista
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users2 = gson.fromJson(json, userListType);
System.out.println(users2.get(0).getName()); // Alice
}
}
Nota importante: per deserializzare collezioni usa TypeToken<>!
3. Configurare Gson: GsonBuilder
Gson offre una configurazione flessibile tramite la classe GsonBuilder. Con essa puoi attivare il pretty printing, la serializzazione di null, la formattazione delle date e altro ancora.
Esempio: pretty printing e serializzazione di null
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonBuilderExample {
public static void main(String[] args) {
User user = new User("Charlie", 0, false);
Gson gson = new GsonBuilder()
.setPrettyPrinting() // Output formattato (indentazione)
.serializeNulls() // Serializzare i campi null
.create();
String json = gson.toJson(user);
System.out.println(json);
/*
{
"name": "Charlie",
"age": 0,
"active": false
}
*/
}
}
Formattazione delle date
Se hai campi di tipo Date, per impostazione predefinita Gson li serializza in un formato specifico. Puoi impostare il tuo formato:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;
public class DateExample {
private String event;
private Date date;
public DateExample(String event, Date date) {
this.event = event;
this.date = date;
}
}
public class Main {
public static void main(String[] args) {
DateExample meeting = new DateExample("Team Meeting", new Date());
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
String json = gson.toJson(meeting);
System.out.println(json);
// {"event":"Team Meeting","date":"2024-06-10 13:45:23"}
}
}
4. Annotazioni Gson: controllo della serializzazione
Gson supporta annotazioni per un controllo più preciso della serializzazione e deserializzazione.
@SerializedName — rinominare un campo
Se vuoi che un campo in JSON abbia un altro nome, usa @SerializedName:
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("full_name")
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Ora durante la serializzazione il campo si chiamerà full_name:
User user = new User("Diana", 28, true);
String json = new Gson().toJson(user);
// {"full_name":"Diana","age":28,"active":true}
@Expose — serializzare solo i campi contrassegnati
Se vuoi serializzare solo determinati campi, usa @Expose e configura Gson:
import com.google.gson.annotations.Expose;
public class User {
@Expose
private String name;
@Expose
private int age;
private boolean active; // non viene serializzato
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Creiamo un Gson con supporto per @Expose:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
User user = new User("Eve", 21, false);
String json = gson.toJson(user);
// {"name":"Eve","age":21}
@Since/@Until — serializzazione condizionale per versione
Puoi usare @Since e @Until per serializzare campi solo per determinate versioni (raramente usato in pratica, ma utile da conoscere).
5. Caratteristiche e limiti di Gson
Lavorare con oggetti annidati
Gson lavora perfettamente con oggetti annidati:
public class Profile {
private User user;
private String bio;
public Profile(User user, String bio) {
this.user = user;
this.bio = bio;
}
}
Profile profile = new Profile(new User("Frank", 27, true), "Java developer");
String json = new Gson().toJson(profile);
// {"user":{"name":"Frank","age":27,"active":true},"bio":"Java developer"}
Lavorare con le collezioni
Con la serializzazione delle collezioni (List, Map) non ci sono problemi, ma per la deserializzazione usa TypeToken (vedi sopra).
Limitazioni di Gson rispetto a Jackson
- Nessun supporto per le classi record di Java (fino alle versioni più recenti)
- Supporto limitato per le nuove API di tipi data/ora (ad esempio, LocalDate, LocalDateTime — servono adapter personalizzati)
- Non supporta le annotazioni di Jackson
- Nessun supporto per strutture polimorfe complesse «out of the box»
- Nessun supporto automatico per i riferimenti bidirezionali (ciclicità)
Adapter personalizzati (TypeAdapter)
Se le funzionalità standard non bastano, puoi scrivere un tuo adapter per serializzare/deserializzare tipi complessi.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class BooleanAsIntAdapter extends TypeAdapter<Boolean> {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value ? 1 : 0);
}
@Override
public Boolean read(JsonReader in) throws IOException {
return in.nextInt() == 1;
}
}
// Utilizzo:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.create();
6. Pratica: serializzazione e deserializzazione con impostazioni
Sviluppiamo la tua applicazione didattica e aggiungiamo salvataggio e caricamento di un elenco di utenti in formato JSON.
Classe User con annotazioni
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
public class User {
@Expose
@SerializedName("full_name")
private String name;
@Expose
private int age;
private boolean active; // non viene serializzato
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getter, setter...
}
Salviamo un elenco di utenti in JSON
import java.util.List;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class SaveUsers {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Ivan", 23, true),
new User("Olga", 19, false)
);
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create();
String json = gson.toJson(users);
System.out.println(json);
/*
[
{
"full_name": "Ivan",
"age": 23
},
{
"full_name": "Olga",
"age": 19
}
]
*/
}
}
Carichiamo un elenco di utenti da JSON
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public class LoadUsers {
public static void main(String[] args) {
String json = "[{\"full_name\":\"Ivan\",\"age\":23},{\"full_name\":\"Olga\",\"age\":19}]";
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(json, userListType);
for (User user : users) {
System.out.println(user.getName() + " (" + user.getAge() + ")");
}
// Ivan (23)
// Olga (19)
}
}
7. Confronto tra Gson e Jackson
| Caratteristica | Gson | Jackson |
|---|---|---|
| Semplicità d’uso | +++++ (molto semplice) | +++ (un po’ più complesso) |
| Dimensioni della libreria | Piccola | Più grande |
| Velocità | Veloce, ma leggermente più lenta | Molto veloce |
| Flessibilità | Media | Alta (più opzioni) |
| Supporto annotazioni | Proprie (@SerializedName) | Proprie (@JsonProperty e altri) |
| Supporto per i nuovi tipi | Limitata | Ottima (Java 8+, record) |
| Supporto Android | Eccellente | Buono, ma più pesante |
| Gestione delle date | Solo tramite adapter | Pronta all’uso |
| Polimorfismo | Limitato | Configurabile in modo flessibile |
8. Errori tipici nell’uso di Gson
Errore n. 1: non usi TypeToken per le collezioni.
Se deserializzi una lista o una mappa, usa obbligatoriamente TypeToken<>, altrimenti otterrai errori strani o collezioni vuote.
Errore n. 2: manca il costruttore senza parametri.
Gson può funzionare anche senza costruttore di default, ma a volte nella deserializzazione di oggetti complessi senza tale costruttore possono sorgere errori. È meglio aggiungerne sempre uno se prevedi la deserializzazione.
Errore n. 3: i nomi dei campi non corrispondono.
Se nel JSON il campo si chiama "full_name", e nella classe — "name", senza l’annotazione @SerializedName("full_name") il campo non verrà associato e il valore sarà null.
Errore n. 4: problemi con i campi privati.
Gson può serializzare campi privati, ma se ci sono solo campi privati e non ci sono getter/setter, a volte si verificano problemi in deserializzazione. È meglio usare getter e setter.
Errore n. 5: gestione delle date.
Per impostazione predefinita Gson serializza Date in un formato poco comodo. Per LocalDate, LocalDateTime e altri nuovi tipi, senza adapter personalizzati si avranno errori di serializzazione.
Errore n. 6: non usi @Expose, ma hai attivato excludeFieldsWithoutExposeAnnotation().
Se hai attivato excludeFieldsWithoutExposeAnnotation() ma non hai contrassegnato i campi con @Expose, essi non verranno serializzati né deserializzati — il risultato sarà JSON vuoto o oggetti con null.
GO TO FULL VERSION