1. Introduzione
Per quanto si possa sperare che le collezioni si serializzino e deserializzino perfettamente "out of the box", nei progetti reali spesso non è così. A volte bisogna nascondere certe collezioni dalla serializzazione — per esempio dati interni in cache. Altre volte è necessario rinominare proprietà collezione in modo che corrispondano al contratto API. In alcuni casi è importante controllare quali elementi vengono salvati o ignorati, o anche trasformare la collezione in modo che il JSON risultante sia comprensibile e "leggibile" per altri servizi.
Per fortuna System.Text.Json offre un modo semplice e trasparente per gestire la serializzazione tramite attributi che puoi applicare sia alle collezioni sia ai singoli elementi. In questa sezione continueremo a sviluppare il nostro modello di libreria per capire come funziona nella pratica.
2. Escludere proprietà collezione: [JsonIgnore]
Iniziamo dal semplice. A volte nella tua classe c'è una collezione che non deve essere serializzata — ad esempio è temporanea, in cache o contiene dati sensibili. Cosa fare? Ovviamente, [JsonIgnore]!
Immaginiamo di avere una classe Library a cui abbiamo aggiunto la proprietà List<Book> Cache, usata solo per accesso rapido:
using System.Text.Json.Serialization;
public class Library
{
public string Name { get; set; }
public List<Book> Books { get; set; }
[JsonIgnore]
public List<Book> Cache { get; set; } // Non viene serializzato!
}
// Esempio d'uso:
var library = new Library
{
Name = "Biblioteca principale",
Books = new List<Book>
{
new Book { Title = "La valle magica", Author = new Author { Name = "Tove Jansson", BirthYear = 1914 } }
},
Cache = new List<Book>
{
new Book { Title = "Il signore delle mosche", Author = new Author { Name = "William Golding", BirthYear = 1911 } }
}
};
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json); // Nel JSON non c'è la proprietà Cache!
Il risultato della serializzazione sarà più o meno così:
{
"Name": "Biblioteca principale",
"Books": [
{
"Title": "La valle magica",
"Author": {
"Name": "Tove Jansson",
"BirthYear": 1914
}
}
]
}
Vedi? Niente "cache" nel mondo esterno. Tutto ciò che è sotto [JsonIgnore] è nascosto e sicuro, come la password del Wi‑Fi nella tua testa.
3. Rinominare le collezioni con [JsonPropertyName]
Ti capita spesso di avere API che si aspettano, per esempio, "items" invece di "Books"? O magari non vuoi rinominare il campo in C# (per non confondere il codice), ma nel JSON deve "chiamarsi" diversamente?
Ecco come si fa:
using System.Text.Json.Serialization;
public class Library
{
public string Name { get; set; }
[JsonPropertyName("items")]
public List<Book> Books { get; set; }
[JsonIgnore]
public List<Book> Cache { get; set; }
}
// Serializzazione:
var library = new Library { Name = "Filiale №1", Books = new List<Book>() };
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
Output:
{
"Name": "Filiale №1",
"items": []
}
Nota che anche la deserializzazione mapperà correttamente il campo items dal JSON a Books in C# — la magia funziona in entrambe le direzioni.
4. Gestire la serializzazione delle collezioni e dei loro elementi
Per questo ti serviranno JsonIgnoreCondition.WhenWritingNull e/o tipi nullable.
Può capitare che una collezione sia semplicemente un campo opzionale. Per esempio, una biblioteca appena creata non ha ancora libri. Se non vuoi che nel JSON compaia la proprietà books: null, puoi gestirlo tramite le opzioni:
var library = new Library { Name = "Biblioteca vuota" };
// Books non inizializzato = null
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true
};
string json = JsonSerializer.Serialize(library, options);
Console.WriteLine(json);
Risultato:
{
"Name": "Biblioteca vuota"
}
Se invece hai una lista vuota (ma non null), il serializzatore emetterà "books": []. Questa è una differenza importante, perché a volte vuoi nascondere il campo quando è null, ma non quando è una lista vuota intenzionale.
5. Attributo [JsonIgnore] sulle proprietà degli elementi
Gli attributi di serializzazione funzionano anche all'interno degli elementi della collezione. Puoi nascondere singole proprietà di ogni oggetto nella lista.
public class Book
{
public string Title { get; set; }
public Author Author { get; set; }
[JsonIgnore]
public string InternalCode { get; set; }
}
Ora, quando serializzi un libro dalla collezione Books, la proprietà InternalCode non sarà nel JSON.
6. Mappare collezioni tramite "indici" o strutture annidate
A volte devi serializzare collezioni non semplicemente come array ma, per esempio, come "mappe" (dictionary) — se ogni libro ha un identificatore unico. In questo caso — senza attributi particolari sugli elementi, ma usando gli strumenti standard — puoi dichiarare una proprietà dizionario:
public class Library
{
[JsonPropertyName("catalog")]
public Dictionary<string, Book> BookCatalog { get; set; }
}
Alla serializzazione il dizionario diventerà un oggetto con coppie chiave-valore:
var library = new Library
{
BookCatalog = new Dictionary<string, Book>
{
["978-5-699-12345-6"] = new Book { Title = "Dizionario", Author = new Author { Name = "Sconosciuto", BirthYear = 2000 } }
}
};
JSON:
{
"catalog": {
"978-5-699-12345-6": {
"Title": "Dizionario",
"Author": {
"Name": "Sconosciuto",
"BirthYear": 2000
}
}
}
}
Questa rappresentazione è comoda per l'API quando è importante mantenere la relazione tra la chiave e l'oggetto.
7. Errori e difficoltà nel gestire la serializzazione delle collezioni
Se provi a serializzare una collezione i cui elementi non sono tutti correttamente inizializzati (per esempio nella lista ci sono null), di default System.Text.Json scriverà quegli elementi come null nell'array.
Anche se hai impostato DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, gli elementi-null dentro l'array rimarranno — la regola si applica alle proprietà dell'oggetto, non al contenuto delle collezioni. Per evitarlo, pulisci la collezione prima: RemoveAll(b => b == null).
Una confusione comune durante la deserializzazione è il disallineamento dei nomi. Se dimentichi di usare [JsonPropertyName], la classe si aspetterà la proprietà Books, mentre tu invii JSON con items: di conseguenza la collezione non verrà popolata e rimarrà vuota. Controlla sempre i nomi!
8. Tabella: dove si applicano i principali attributi
| Attributo | Si può applicare alle collezioni? | Si può applicare agli elementi delle collezioni? | Esempi d'uso |
|---|---|---|---|
|
sì | sì | Nascondere la lista o un campo dentro Book |
|
sì | sì | Rinominare Books → items o Title → name |
|
sì | sì | Includere proprietà private nella serializzazione |
|
sì | sì | Assegnare un converter speciale alla lista |
9. Schema di serializzazione delle collezioni con attributi
+-------------+
| Library |
+-------------+
| Name -- viene serializzato come "Name"
| Books -- [JsonPropertyName("items")], viene serializzato come "items": [...]
| Cache -- [JsonIgnore], non viene serializzato
| BookCatalog -- [JsonPropertyName("catalog")], viene serializzato come "catalog": {...}
Risultato JSON più o meno così:
{
"Name": "Biblioteca cittadina",
"items": [
{
"Title": "1984",
"Author": {
"Name": "George Orwell",
"BirthYear": 1903
}
},
{
"Title": "Grandi speranze",
"Author": {
"Name": "Charles Dickens",
"BirthYear": 1812
}
}
],
"catalog": {
"978-1234567890": {
"Title": "Il richiamo di Cthulhu",
"Author": {
"Name": "Howard Phillips Lovecraft",
"BirthYear": 1890
}
}
}
}
10. Valore pratico e aspetti per colloqui e progetti reali
In situazioni "di produzione" devi sempre considerare il contratto dell'API esterna e i requisiti di serializzazione. Bisogna saper "nascondere" collezioni interne, rispettare il case e lo stile dei nomi, e talvolta cambiare dinamicamente lo schema di serializzazione in base alla versione del client.
Domande tipiche da colloquio:
- Come serializzare solo una parte dei dati?
- Come fare in modo che la proprietà della collezione non appaia nel JSON?
- Come mappare i nomi delle proprietà tra C# e JSON se sono diversi?
- È possibile nascondere singoli elementi dentro una collezione dalla serializzazione (per esempio informazioni confidenziali)?
Le risposte ruotano attorno all'uso corretto di attributi e opzioni di serializzazione: [JsonIgnore], [JsonPropertyName], le opzioni di JsonSerializerOptions e a una gestione attenta del contenuto delle collezioni.
GO TO FULL VERSION