1. Introduzione
Promemoria: La serializzazione è un modo per salvare lo stato di un oggetto come stringa, file o stream, così poi si può ricostruire quell'oggetto (deserializzare) — e si ottiene di nuovo l'oggetto con gli stessi dati.
Situazione tipica:
1) Hai un oggetto, per esempio un utente con nome ed età.
2) Devi salvarlo — inviarlo in rete, scriverlo su file o, diciamo, nel formato JSON.
3) Poi prendi i dati dalla stringa/file e li ricostruisci — e ottieni di nuovo l'oggetto!
In C# si fa in poche righe. Vediamo come.
La nostra applicazione
Stiamo sviluppando una piccola app "Contact Manager". Supponiamo di avere la classe Person che vogliamo serializzare.
public class Person
{
// Le proprietà pubbliche sono necessarie per la serializzazione!
public string Name { get; set; }
public int Age { get; set; }
// Il costruttore senza parametri è obbligatorio per XmlSerializer
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Se non conosci questa sintassi, non preoccuparti — più avanti nel corso analizzeremo classi e proprietà in dettaglio. Per ora consideralo come modello dati per gli esperimenti. Nota: le proprietà pubbliche Name e Age sono importanti per la serializzazione, e per XmlSerializer serve un costruttore pubblico senza parametri.
2. Serializzazione in JSON con System.Text.Json
Esempio minimale
Promesso — eccolo! Così si serializza in JSON:
using System;
using System.Text.Json;
Person person = new Person("Alisa", 28);
// Serializzazione: trasformiamo l'oggetto in una stringa JSON
string json = JsonSerializer.Serialize(person);
Console.WriteLine("JSON:");
Console.WriteLine(json);
Cosa vedrai in output?
JSON:
{"Name":"Alisa","Age":28}
Tutto semplice: oggetto → JSON. Ecco la magia!
Deserializzazione — riportiamo tutto indietro
Ora facciamo l'operazione inversa — dalla stringa JSON ricostruiamo l'oggetto:
string json = "{\"Name\":\"Bob\",\"Age\":35}";
// Deserializzazione: ricostruiamo un oggetto di tipo Person dalla stringa JSON
Person restored = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine("Oggetto ricostruito:");
Console.WriteLine($"Nome: {restored.Name}, età: {restored.Age}");
Risultato:
Oggetto ricostruito:
Nome: Bob, età: 35
Nota: se la struttura dei dati non combacia (per esempio manca una proprietà), il campo corrispondente rimarrà con il valore di default (null per i tipi reference, 0 per i numeri, ecc.).
3. Serializzazione/deserializzazione in XML con XmlSerializer
Serializzare in XML — prima occhiata
using System;
using System.IO;
using System.Xml.Serialization;
Person person = new Person("Katja", 22);
// Creiamo il serializer per il tipo Person
var serializer = new XmlSerializer(typeof(Person));
// Scriviamo l'XML in un file
using (var stream = new FileStream("person.xml", FileMode.Create))
{
serializer.Serialize(stream, person);
// Lo stream si chiuderà automaticamente al termine del using
}
Console.WriteLine("File XML creato!");
Nel file person.xml apparirà qualcosa del genere:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Katja</Name>
<Age>22</Age>
</Person>
Punto importante:
Per serializzare in file si usa uno stream (Stream, ad es. FileStream). Se vuoi serializzare in una stringa (per invio in rete), usa StringWriter:
var serializer = new XmlSerializer(typeof(Person));
using (var sw = new StringWriter())
{
serializer.Serialize(sw, person);
string xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
Deserializzare da XML
using (var stream = new FileStream("person.xml", FileMode.Open))
{
// Non dimenticare il casting al tipo corretto!
Person restored = (Person)serializer.Deserialize(stream);
Console.WriteLine($"Nome: {restored.Name}, età: {restored.Age}");
}
Ancora: XmlSerializer richiede che la classe serializzabile abbia un costruttore pubblico di default (senza parametri).
4. Lavorare con file e stringhe: cosa scegliere?
Nella vita reale la serializzazione può essere direttamente in una stringa (per invio in rete/salvataggio in DB) oppure in un file (per storage a lungo termine).
Serializzazione in stringa (per esempio per invio via API):
- JSON: usa JsonSerializer.Serialize(obj).
- XML: tramite StringWriter.
Serializzazione in file (per esempio per export/backup):
- JSON: salva la stringa su file con gli strumenti standard di file I/O.
- XML: scrivi direttamente nello stream con XmlSerializer.
Esempio: serializzare su file e leggere (JSON)
using System.IO;
using System.Text.Json;
Person person = new Person("Tom", 42);
// Serializzazione in stringa
string json = JsonSerializer.Serialize(person);
// Scrittura su file
File.WriteAllText("contact.json", json);
// Lettura dal file
string readJson = File.ReadAllText("contact.json");
Person restored = JsonSerializer.Deserialize<Person>(readJson);
Console.WriteLine($"{restored.Name}, età: {restored.Age}");
5. Confronto JSON vs XML
Visualizzazione del processo
Per non confondersi, ecco lo schema del processo di serializzazione e deserializzazione:
flowchart LR
A[Oggetto] -- serializzazione --> B[Stringa/File/Stream]
B -- deserializzazione --> C[Oggetto]
A -. JSON o XML .-> B
B -. JSON o XML .-> C
Puoi pensare alla serializzazione come "fare la valigia", e alla deserializzazione come "disfare la valigia" a casa.
Tabella di confronto
| Funzionalità | JSON (System.Text.Json) | XML (XmlSerializer) |
|---|---|---|
| Lettura/scrittura in stringa | ++ | + |
| Lettura/scrittura in file | ++ | ++ |
| Richiede costruttore senza parametri | No | Sì |
| Readability per umani | ++ | + |
| Schema di dati rigido (validazione) | - | ++ |
| Flessibilità (attributi di customizzazione) | + | ++ |
| Performance | ++ | + |
| Compatibilità con internet | ++ | + |
++ — molto bene, + — normale, - — non supportato
6. Come aggiungere la serializzazione alla nostra app
Immaginiamo che in "Contact Manager" sia stata aggiunta la funzione per salvare la lista di contatti su file e ripristinarla.
Contesto: abbiamo una lista di contatti (List<Person>).
List<Person> contacts = new List<Person>
{
new Person("Vasya", 30),
new Person("Petya", 25),
new Person("Masha", 27)
};
// Salviamo tutti i contatti — serializziamo in JSON
string jsonContacts = JsonSerializer.Serialize(contacts);
File.WriteAllText("contacts.json", jsonContacts);
// Ripristiniamo la lista di contatti
string readContactsJson = File.ReadAllText("contacts.json");
List<Person> restored = JsonSerializer.Deserialize<List<Person>>(readContactsJson);
foreach (var c in restored)
{
Console.WriteLine($"{c.Name}, età: {c.Age}");
}
La stessa cosa si può fare con XML, ma ci sono delle particolarità. Per serializzare collezioni con XmlSerializer spesso si avvolge la lista in una classe contenitore (elemento radice) — così è più semplice controllare il formato e i nomi degli elementi.
7. Errori tipici durante serializzazione e deserializzazione
Errore №1: mismatch dei tipi durante serializzazione/deserializzazione.
Hai serializzato List<Person> — devi deserializzare nello stesso tipo List<Person>, non in List<object> o IEnumerable<Person>. Altrimenti otterrai un'eccezione o risultati inattesi.
Errore №2: tentare di serializzare campi/proprietà non pubblici.
I serializer tipici (JsonSerializer, XmlSerializer) lavorano con membri pubblici. Se una proprietà non ha getter/setter pubblico, il valore potrebbe non finire nel JSON/XML, e ti chiederai perché è null.
Errore №3: mancanza del costruttore senza parametri.
Per XmlSerializer il costruttore pubblico di default è obbligatorio. Se non c'è — si verifica un'eccezione in fase di deserializzazione.
Errore №4: fraintendere i riferimenti agli oggetti.
Se un oggetto contiene riferimento a un altro, la serializzazione avviene "per valore" (come oggetto annidato). I riferimenti in sé non sono preservati, il che può causare duplicazione dei dati o problemi con riferimenti ciclici.
Errore №5: cercare di mescolare formati di serializzazione.
XML e JSON sono formati diversi con serializer diversi. Non puoi passare una stringa JSON a XmlSerializer e viceversa — otterrai un errore.
GO TO FULL VERSION