1. Introduzione
Abbiamo già lavorato con le collezioni. Non si può farne a meno in programmazione, né nella vita quotidiana. Immagina di fare la lista delle cose da fare per la giornata: comprare generi, chiamare il medico, ritirare un ordine. Dubito che userai un taccuino diverso per ogni attività. È molto più semplice e logico mettere tutto in un'unica lista.
Lo stesso vale in programmazione: quando gli oggetti diventano tanti — utenti, ordini, messaggi — non creiamo una variabile separata per ognuno. Usiamo le collezioni: liste, dizionari, set. Questo permette di memorizzare, iterare e processare gruppi di dati in modo comodo.
Scenari dalla vita reale:
- Memorizzazione e scambio di dati tra applicazioni: la tua collezione di libri può migrare da un programma all'altro.
- Caching di dataset su disco.
- Trasferimento di dati in rete (frontend ↔ backend).
- Import/export di informazioni (ad esempio, decidi di fare il tuo export di libri in JSON!).
A un colloquio: «Come serializzeresti e salveresti una lista di ordini?» — farebbe una buona impressione se non parli solo di singoli oggetti ma anche della serializzazione delle collezioni!
2. Come funzionano le collezioni durante la serializzazione
JSON e collezioni: una breve storia d'amore
Quando serializzi un oggetto normale, diventa un JSON-object del tipo { "field": value }. Ma se serializzi una lista o un array, ottieni un JSON-array [ ... ].
Visivamente:
| C# | JSON |
|---|---|
|
|
|
|
|
|
La magia principale: Chiami JsonSerializer.Serialize() su una collezione — e la trasforma automaticamente in un array! Al contrario Deserialize<List<T>>() — e la magia funziona di nuovo.
Corrispondenza tra collezioni C# e JSON-array
| Tipo di collezione in C# | Esempio | JSON |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Esempio: serializzazione e deserializzazione di un array di libri
Iniziamo con un esempio minimo. Prendiamo la nostra classe Book, che abbiamo già scritto nelle lezioni precedenti:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
Ora creiamo un array di libri, serializziamolo, salviamolo su file e ricarichiamolo.
using System;
using System.IO;
using System.Text.Json;
namespace LibraryApp
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
class Program
{
static void Main()
{
// Creiamo un array di libri
Book[] books = new Book[]
{
new Book { Title = "Tri tovarishcha", Author = "Erikh Mariya Remark" },
new Book { Title = "Master i Margarita", Author = "Mikhail Bulgakov" },
new Book { Title = "1984", Author = "Dzhordzh Oruell" }
};
// Serializzazione dell'array di libri in una stringa
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(books, options);
Console.WriteLine("JSON dell'array di libri:\n" + json);
// Scrittura su file (sincrona)
File.WriteAllText("books.json", json);
// Lettura da file (sincrona)
string jsonFromFile = File.ReadAllText("books.json");
// Deserializzazione indietro in array
Book[]? booksFromFile = JsonSerializer.Deserialize<Book[]>(jsonFromFile);
// Stampiamo il risultato sulla console
Console.WriteLine("\nLibri deserializzati:");
if (booksFromFile != null)
{
foreach (var book in booksFromFile)
{
Console.WriteLine($"- {book.Title} (autore: {book.Author})");
}
}
}
}
}
Cosa succede qui?
- Creiamo un array Book[], lo riempiamo con tre libri.
- Con JsonSerializer.Serialize trasformiamo l'array in una stringa JSON leggibile (l'opzione WriteIndented rende il formato più leggibile).
- La scriviamo su file, la leggiamo indietro e la deserializziamo — et voilà, abbiamo di nuovo l'array di libri!
- Controlliamo che tutti i dati siano stati ripristinati correttamente.
Controlla che il file "books.json" compaia nella cartella del programma. Aprilo: vedrai un JSON simile a questo:
[
{
"Title": "Tri tovarishcha",
"Author": "Erikh Mariya Remark"
},
{
"Title": "Master i Margarita",
"Author": "Mikhail Bulgakov"
},
{
"Title": "1984",
"Author": "Dzhordzh Oruell"
}
]
4. Esempio: serializzazione della collezione List<T>
Lavorare con List<Book> è identico. Il serializer riconosce la collezione e la converte in un JSON-array.
List<Book> myBooks = new List<Book>
{
new Book { Title = "Prestuplenie i nakazanie", Author = "Fyodor Dostoevskiy" },
new Book { Title = "Voyna i mir", Author = "Lev Tolstoy" }
};
string jsonList = JsonSerializer.Serialize(myBooks, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonList);
// E deserializziamo così:
List<Book>? loadedBooks = JsonSerializer.Deserialize<List<Book>>(jsonList);
// Controlliamo che tutto sia tornato!
foreach (var book in loadedBooks!)
{
Console.WriteLine($"{book.Title} ({book.Author})");
}
Si può serializzare una lista di interi?
Certo! La serializzazione funziona non solo per le tue classi, ma anche per tipi primitivi:
List<int> numbers = new List<int> { 10, 20, 30, 40 };
string jsonNums = JsonSerializer.Serialize(numbers); // risultato: [10,20,30,40]
List<int>? loadedNums = JsonSerializer.Deserialize<List<int>>(jsonNums);
// loadedNums: List<int> con gli stessi valori
5. Schema e analogie: come vede le collezioni il serializer?
Per capire meglio come funziona la serializzazione delle collezioni, guardiamo uno schema semplice:
graph TD;
A[List[Book] in C#] -->|Serialize| B[JSON-array in stringa]
B -->|Write| C[File books.json]
C -->|Read| D[Stringa JSON dal file]
D -->|Deserialize| E[List[Book] in C#]
Breve descrizione del processo:
- Una collezione o un array viene serializzato come array in JSON ([ ... ]).
- L'ordine degli elementi è preservato (a meno che non usi impostazioni particolari).
- Ogni elemento della collezione viene serializzato come il proprio oggetto.
6. Caratteristiche della serializzazione delle collezioni: a cosa fare attenzione
1. null e collezioni vuote
Se la collezione è null, il serializer per default scriverà semplicemente null nel JSON. Fai attenzione: nella logica di business una collezione vuota e null possono significare cose diverse!
Se la collezione è vuota (new List<Book>()), il JSON sarà [] — un array vuoto. Questo è comodo quando vuoi mostrare esplicitamente l'assenza di elementi.
2. Deserializzazione — l'ordine conta
L'ordine degli elementi nell'array viene sempre mantenuto. Quindi se hai serializzato tre libri in un certo ordine, lo stesso ordine verrà ricostruito.
3. Collezioni con oggetti di tipi diversi?
System.Text.Json non supporta il polimorfismo "out-of-the-box". Quindi se hai, per esempio, una List<Animal> con Dogs e Cats (ognuno è una classe derivata), per default non puoi ricavare "qual è il tipo esatto" durante la deserializzazione. Arriveremo alla serializzazione polimorfica più avanti, ma con liste normali è tutto semplice.
7. Errori tipici e fraintendimenti comuni
Errore n.1: Serializzi la collezione ma dimentichi che può contenere oggetti con campi privati
JsonSerializer serializza solo le proprietà pubbliche (e solo con getter/setter). Se la tua classe è così:
public class User
{
public string Login { get; set; }
private string Password { get; set; } // Non verrà serializzato!
}
La password non finirà nel JSON, ed è una cosa utile per la sicurezza. Ma se volevi salvarla — non dimenticare di rendere la proprietà pubblica.
Errore n.2: Serializzazione di collezioni con elementi null
Se la collezione contiene null, ad esempio: new List<Book> { null, book2 }, allora dopo la serializzazione il primo elemento sarà null. Alla deserializzazione — succederà la stessa cosa! Esempio di JSON:
[null, { "Title": "Voyna i mir", "Author": "Lev Tolstoy" }]
In pratica succede raramente, ma se serializzi collezioni che possono avere "buchi" — tienilo in conto nella logica.
Errore n.3: Tipo sbagliato durante la deserializzazione
Errore comune: serializzi una lista e poi la deserializzi in un array (o viceversa). Questo funziona se i tipi sono compatibili (Book[] ↔ List<Book>), ma possono esserci dettagli da considerare, per esempio se provi a deserializzare un array di stringhe in un oggetto non-collezione.
GO TO FULL VERSION