CodeGym /Corsi /JAVA 25 SELF /Gson — serializzazione e deserializzazione, configurazion...

Gson — serializzazione e deserializzazione, configurazione

JAVA 25 SELF
Livello 46 , Lezione 2
Disponibile

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.

1
Compito
JAVA 25 SELF, livello 46, lezione 2
Bloccato
Sincronizzazione dei dati degli studenti con un sistema esterno 🎓
Sincronizzazione dei dati degli studenti con un sistema esterno 🎓
1
Compito
JAVA 25 SELF, livello 46, lezione 2
Bloccato
Creazione di profili pubblici in un social network 🌐
Creazione di profili pubblici in un social network 🌐
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION