Introduzione
La serializzazione è un 
processo tramite il quale trasformiamo l'istanza di una classe in un formato 
trasportabile. Il formato può essere uno stream di tipo XML o di tipo binario, 
dipendentemente dal tipo di serializzazione che intendiamo ottenere. 
Il formato XML ha diversi vantaggi: 
innanzitutto è human-readable, quindi può essere facilemente essere letto con un 
qualsiasi editor di testi (più o meno specializzato per il trattamento di 
documenti XML). In secondo luogo, uno stream XML può facilmente essere 
trasmesso ad un web-services via http, dato che non contiene caratteri speciali 
che potrebbero essere filtrati o bloccati da regole impostate in un eventuale 
firewall.
Il formato binario invece, come 
dice il nome stesso, serializza l'istanza di classe in un più generico stream di 
bytes. E' un formato più compatto rispetto alla serializzazione XML. Se 
decidiamo di serializzare in formato binario abbiamo un grande vantaggio: 
possiamo intercettare la richiesta di serializzazione 
della classe ed intervenire, customizzando completamente il modo con cui il FX 
serializzerebbe di default. D'altro canto, il formato binario per sua natura 
potrebbe contenere caratteri anomali(passatemi il termine) che 
potrebbe venir bloccato da eventuali firewall: questo lo rende poco adatto 
alla trasmissione http.
Serializzazione in formato XML
La serializzazione più 
semplice in assoluto passa attraverso la classe XmlSerializer del FX. Nel codice che ho messo a disposizione c'è 
già una classe XMLSerialization con tutta una serie di metodi statici che 
serializzano le classi coinvolte dal progetto (Storage, Shelf, Book e Chapter): 
vi rimando a quel codice per vedere ed esaminare concretamente il funzionamento. 
La cosa importante da sapere è che ogni classe .NET serializza soltanto le 
proprietà pubbliche di cui dispone. Se va bene il comportamento di default che 
ci offre il FX, possiamo portare a termine la serializzazione XML in modo molto 
semplice in questo modo:
static public void Serialize(Chapter cp, string fileName)
{
    if (regole == null)
        SetupWriterSettings();
    XmlWriter wri = XmlWriter.Create(fileName, regole);
    XmlSerializer ser = new XmlSerializer(typeof(Chapter));
    ser.Serialize(wri, cp);
    wri.Close();
}
La classe XmlWriter del FX ci permette di ottenere un oggetto con il quale 
gestire uno stream XML: nell'esempio qui sopra, lo usiamo per scrivere 
fisicamente un file su disco. La chiamata a SetupWriterSettings() inizializza 
un'istanza della classe XmlWriterSettings, un oggetto che uso spesso per definire alcune 
proprietà relative al modo con cui il FX genererà lo stream (indentazione, 
encoding, creare una nuova linea sugli attributi, etc.). Il codice che 
inizializza il mio oggetto regole è questo:
private static void SetupWriterSettings()
{
    regole = new XmlWriterSettings();
    regole.Indent = true;
    regole.IndentChars = "    ";
    regole.ConformanceLevel = ConformanceLevel.Auto;
    regole.NewLineOnAttributes = false;
    regole.Encoding = System.Text.Encoding.Unicode;
}
Successivamente, istanzio un XmlSerializer, indicando nel suo 
costruttore la classe che sto serializzando. Il processo vero e proprio viene 
portato a termine dalla linea:
ser.Serialize(wri, cp);
Questa riga serializza l'oggetto cp (Chapter) nello 
stream indicato da wri (XmlWriter). Nulla di più, nulla di 
meno. Ovviamente, a questo punto possiamo andare a dare un'occhiata al file 
generato, aprirlo con un editor qualsiasi e dare un'occhiata a quanto generato. 
Ad esempio, un'istanza di classe Chapter viene resa nel seguente formato 
XML:
<?xml version="1.0" encoding="utf-16"?>
<Chapter
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Title>Prologo</Title>
</Chapter>
Ovviamente, oggetti più complessi creano un flusso XML più complesso. Ecco un 
esempio dello stream XML generato da un'istanza della classe Book.
<?xml version="1.0" encoding="utf-16"?>
<Book
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Title>La Spada di Shannara</Title>
    <Pages>0</Pages>
    <InLoan>false</InLoan>
    <Bookmark>0</Bookmark>
    <Chapters>
        <Chapter>
            <Title>Prologo</Title>
        </Chapter>
        <Chapter>
            <Title>Inizio</Title>
        </Chapter>
    </Chapters>
</Book>
Nel suo comportamento di default, il FX serializza tutte le public 
properties della classe. Tramite una serie di attributi possiamo decorare ogni 
property per modificare questo comportamento. Ad esempio, possiamo stabilire 
che la property Author della classe Book 
venga serializzata con il tag <Autore> (XmlElementAttribute). Oppure, ancora, 
possiamo dire di ignorare una certa property con l'attributo XmlIgnoreAttribute. Vi consiglio una lettura veloce al documento 
"Attributes That Control XML Serialization" per maggiori 
dettagli sugli attributi disponibili nel FX a questo scopo.
Serializzazione binaria
La 
serializzazione binaria viene effettuata tramite l'utilizzo della classe BinaryFormatter nel namespace System.Runtime.Serialization.Formatters.Binary (lo preciso perchè ci 
ho messo una vita prima di capire dove trovarla - viva l'Object Browser, alias 
CTRL W + J  ).
).
static public void SerializeBinary(Chapter cp, string fileName)
{
    FileStream fs = new FileStream(fileName, FileMode.CreateNew);
    BinaryFormatter form = new BinaryFormatter();
    form.Serialize(fs, cp);
    fs.Close();
    fs.Dispose();
}
Questa volta lo stream fs è creato attraverso la classe 
FileStream, mentre la classe BinaryFormatter compie realmente l'operazione di 
serializzazione dell'oggetto cp. Il file generato non è 
human-readable, è più compatto del corrispondente file XML che abbiamo visto 
prima. Anche in questo caso, però, contiene a tutti gli effetti tutte le 
informazioni necessarie al FX per deserializzare, e quindi per ricostruire 
l'istanza dell'oggetto in memoria.
Implementare l'interfaccia 
ISerializable
Nei processi di serializzazione mostrati qui sopra, 
non abbiamo alcuna possibilità di intervenire sul processo stesso. Il FX ci 
viene in aiuto, ha lui il compito di serializzare e a noi va bene così, 
soprattutto nella maggior parte dei casi. Se per qualsiasi motivo vogliamo 
modificare questo comportamento, possiamo utilizzare l'interfaccia ISerializable. Questa interfaccia richiede l'implementazione di un 
solo metodo GetObjectData(), che viene chiamata automaticamente dal 
FX quando viene richiesta la serializzazione della classe stessa. Vediamo nel 
dettaglio come funziona questa cosa.
using System;
[Serializable]
public class Chapter : ISerializable
{
    // codice, codice, codice
    
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
    }
}
Notare che:
  - ho dovuto decorare l'intera classe con l'attributo Serializable (namespace System).
- ho dovuto ovviamente indicare che Chapter implementa ISerializable
- in conseguenza al punto (2), ho dovuto implementare 
  GetObjectData
Il metodo GetObjectData() viene chiamata al momento della serializzazione 
binaria della classe (non viene 
usato se serializziamo con XML). In questo modo agiamo sull'oggetto 
info (classe SerializationInfo) aggiungendo tutte le informazioni che 
intendiamo serializzare sullo stream tramite la chiamata al metodo AddValue (il quale dispone di 
16 overload diversi per ogni value-type)
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Title", _Title);
    info.AddValue("CurrentDate", DateTime.Now.ToShortDateString());
    string user = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    info.AddValue("CurrentUser", user);
}
Nel codice qui sopra, ad esempio, oltre a serializzare la property 
Title (che avrebbe fatto anche il FX di default), aggiungiamo 
la data corrente e l'utente Windows corrente che sta compiendo questa 
operazione. Tutto quello che andiamo ad aggiungere ad info 
verrà a tutti gli effetti serializzato sullo stream.