Per serializzazione si intende la possibilità di poter salvare un oggetto software in un flusso di memoria per poterlo poi salvare su un file o trasmettere attraverso un qualunque tipo di connessione.
Il framework .Net permette questa operazione in modo molto semplice ed immediato (in alcuni casi anche in modo invisibile all'utente come per applicazioni web service).
Uno dei motivi interessanti per usare la serializzazione è la possibilità di salvare una struttura dati organizzata come classi con una sola operazione. Facciamo un esempio di un ipotetico forum di discussione.
Ogni discussione potrebbe essere rappresentata da una classe di questo tipo
public class Discussione
{
private string titolo = ""
private List messaggi = new List();
public string Titolo
{
get { return titolo; }
set { titolo = value; }
}
public List Messaggi
{
get { return messaggi; }
set { messaggi = value; }
}
}
Dove titolo è il nome della discussione e messaggi l'insieme dei messaggi inseriti nella discussione. Ogni messaggio potrebbe poi poi essere così.
public class Messaggio
{
private string autore = ""
private string testo = ""
public string Autore
{
get { return autore; }
set { autore = value; }
}
public string Testo
{
get { return testo; }
set { testo = value; }
}
}
Dove autore è il nome dell'utente che ha scritto il messaggio ed il testo il testo del messaggio. La classe Forum conterrà per semplicità solo la lista delle discussioni.
public class Forum
{
private List discussioni = new List();
public List Discussioni
{
get { return discussioni; }
set { discussioni = value; }
}
}
Tutta la gestione avverrà tramite l'inserimento o la modifica dei dati contenuti nella classe Forum. Dopo aver inserito varie discussioni e messaggi nel forum è arrivato il momento di salvare su file tutto il forum.
using System.Xml.Serialization;
Forum myForum
using (System.IO.FileStream file = new System.IO.FileStream(pathDoveSalvare, System.IO.FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(Forum));
serializer.Serialize(file, myForum);
}
Ecco fatto, tutto il contenuto del forum è ora salvato in un file XML
xml version="1.0"?>
<Forum xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Discussioni>
<Discussione>
<Titolo>Primo ArgomentoTitolo>
<Messaggi>
<Messaggio>
<Autore>RobyAutore>
<Testo>Primo messaggioTesto>
Messaggio>
Messaggi>
<Messaggi>
<Messaggio>
<Autore>RobyAutore>
<Testo>Secondo messaggioTesto>
Messaggio>
Messaggi>
Discussione>
Discussioni>
Forum>
La struttura è creata automaticamente dall'oggetto serializer.
Nel caso di strutture particolarmente complesse, ad esempio dotate di classi ereditate da altre classi si può utilizzare questo overload
XmlSerializer serializer = new XmlSerializer(typeof(Forum),arrayTypes);
In pratica gli si passa un array di tutte le classi che possono far parte della nostra struttura dati.
Il caricamento si chiama deserializzazione:
using (System.IO.FileStream file = new System.IO.FileStream(diag.FileName, System.IO.FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(Forum));
myForum = (Forum)serializer.Deserialize(file);
}
Come vedete è banale. Le uniche accortezze da utilizzare sono la scrittura delle classi da salvare.
- Ciò che viene salvato in una classe sono le property. Se volete salvare e caricare dovrete permettere il get ed il set di quella proprietà. Le variabili private non saranno comunque salvate
- Potete utilizzare i tag per non serializzare una determinata proprietà (ad esempio [XmlIgnore()] sopra la property fa in modo che il serializzatore ignori quel campo).
- Molti oggetti .Net sono serializzabili, ma non tutti (ad esempio la classe Color non viene serializzata).
Schemi XSD
I file XML oltre che usati come formato di salvataggio (per esempio i nuovi formati di Office 2007 sono file zip contenenti file xml) sono usati anche per lo scambio di dati tra applicazioni (ad esempio applicazioni web o di rete). Essendo i file XML aperti e facilmente editabili tramite un editor di testo può essere molto utile sapere che tipo di struttura può contenere. In questo caso si utilizza il formato XSD.
XSD è un formato per la descrizione di dati xml. Viene scritto anch'esso in xml ed ha una sintassi precisa che descrive cosa un file xml può contenere. Un file xsd descrive completamente la tipologia di dati. Ecco ad esempio la parte del file XSD che descrive un messaggio
<xs:complexType name="Messaggio">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1"
name="Autore" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1"
name="Testo" type="xs:string" />
xs:sequence>
xs:complexType>
I formati XSD vengono usati per molti scopi. Il primo è quello di validare un file, verificare quindi se corrisponde o meno alle specifiche date.
Ad esempio per validare un file xml tramite uno schema XSD si procede in questo modo
private bool Validate(string filename)
{
XmlReaderSettings setting = new XmlReaderSettings();
System.Xml.XmlReader reader = null;
try
{
using (XmlTextReader schemaXML = new XmlTextReader(Application.StartupPath + "\\schema.xsd"))
{
setting.Schemas.Add(XmlSchema.Read(schemaXML, new ValidationEventHandler(ValidateCallBack)));
setting.ValidationType = ValidationType.Schema;
setting.ValidationEventHandler += new ValidationEventHandler(ValidateCallBack);
reader = System.Xml.XmlTextReader.Create(filename, setting);
while (reader.Read())
{ }
reader.Close();
}
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
if (reader != null)
reader.Close();
}
}
public void ValidateCallBack(object obj, ValidationEventArgs args)
{
}
Con questa funzione si effettua una lettura del file xml creato nella variabile reader e lo si valida. La classe XMLReaderSetting permette di impostare vari comportamenti per la lettura. L'evento ValidateCallBack sarà chiamato per ogni errore nel file anche se la lettura comunque continuerà. Se non assegnate l'evento alla variabile setting verrà invece lanciata una eccezione.
Generazione degli schemi
Gli schemi XSD possono essere creati automaticamente tramite il tool XSD presente nel prompt dei comandi di visual studio. L'utility permette 2 interessantissime funzioni:
- Generare un file XSD partendo da un assembly (dll o exe)
- Generare le classi C# o VB partendo da un file XSD
Ad esempio da riga di comando nel prompt questo comando genera il file xsd dall'eseguibile serializzazione. Con il comando /t è possibile scegliere quali classi inserire nello schema.
C:\Users\RobyDx\Desktop\SourceCode>xsd serializzazione.exe
/t:Serializzazione.Data.Forum
/t:Serializzazione.Data.Contatto
/t:Serializzazione.Data.Discussione
/t:Serializzazione.Data.Messaggio
/t:Serializzazione.Data.Utente
Al contrario, partendo da uno schema si può creare l'insieme di classi.
C:\Users\RobyDx\Desktop\SourceCode>xsd schema.xsd /c
In questo modo avendo uno schema XSD è possibile in un attimo generare le classi per poter fare la deserializzazione.
Vi lascio un piccolo demo che utilizza solamente una property grid e la serializzazione per gestire dei dati che simulano il file di scambio di un ipotetico forum.
Demo