Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[70-536, #13] Deserializzazione di una classe da uno stream XML o binario (con contorno di Generics)

Nel post di ieri abbiamo visto come serializzare una classe, sia in formato XML sia in formato binario. Abbiamo anche detto che uno degli scopi della serializzazione è quello di rendere trasmissibile un oggetto da un componente all'altro, dove con componente intendo un qualsiasi altro assembly .NET, un altro PC della nostra LAN o un server Web dall'altra parte del pianeta. Si arriva quindi al proposito di voler ricostruire in memoria un oggetto che era stato serializzato, così da poterlo maneggiare anche dopo la trasmissione stessa.

Deserializzazione da stream XML
Questa operazione è nota con il nome di deserializzazione ed è facilmente intuibile, quindi, considerarla come l'operazione inversa della serializzazione. La deserializzazione si attua sempre attraverso la classe XmlSerializer già esaminata ieri, solo che questa volta useremo il metodo Deserialize invece del Serialize. Fin qua, nulla di complicato.  Partendo da questi primi concetti teorici, quindi, possiamo già scrivere un metodo che possa deserializzare la nostra classe Shelf in questo modo:

static public Shelf Deserialize(string fileName)
{
    XmlReader rd = XmlReader.Create(fileName);
    XmlSerializer ser = 
new XmlSerializer(typeof(Shelf));
    
if (ser.CanDeserialize(rd) == true)
        
return ((Shelf)ser.Deserialize(rd));
    
else
        return 
(null);
}

In questo caso abbiamo istanziato un'oggetto XmlReader, che ci permette di aprire uno stream verso il file XML che vogliamo deserializzare. Poi, istanziamo un XmlSerializer e controlliamo che lo stream aperto contenga realmente un qualcosa che possa essere serializzato. Se il metodo CanDeserialize ritorna true, ritorniamo semplicemente un'istanza di Shelf. Se lo stream non può essere deserializzato, ritorniamo al chiamante null. Questo approccio può tornare molto utile se implementato con i generics:

static public T Deserialize<T>(string fileName) where T : new()
{
    XmlReader rd = XmlReader.Create(fileName);
    XmlSerializer ser = 
new XmlSerializer(typeof(T));
    
if (ser.CanDeserialize(rd) == true)
        
return ((T)ser.Deserialize(rd));
    
else
        return 
(default(T));
}

Questo metodo statico inserito in una classe XMLSerialization permette di deserializzare da un qualsiasi file un qualsiasi oggetto, il cui tipo è indicato da T. Ho applicato un constraint al tipo generico T forzando il fatto che deve essere possibile poter creare un'istanza di T tramite la keywork new(). Ecco un esempio di utilizzo:

Shelf mensola = XMLSerialization.Deserialize<Shelf>("D:\\Shelf.xml");
Chapter cp = XMLSerialization.Deserialize<Chapter>("D:\\Chapter.xml");

Come vedete, usiamo sempre lo stesso metodo dalla stessa classe, il quale però essendo generico può ritornare un'istanza di Shelf, Book, Chapter ed in genere (gioco di parole) qualsiasi classe .NET.

Con questi semplici passaggi, quindi, siamo in grado di persistere una classe su uno stream: in questi esempi abbiamo usato un file, ma in realtà potrebbe trattarsi di uno stream di ogni tipo, come MemoryStream, una banale StringBuilder. Date un'occhiata ai diversi overload della classe XmlSerializer per farvi un'idea.

Deserializzazione da stream binario
La stessa logica con i generics può essere utilizzata per la deserializzazione da uno stream binario. Ovviamente cambiano alcune classi in gioco, ed dobbiamo modificare il codice per alcuni accorgimenti per fare in modo che tutto funzioni regolarmente:

  1. il file binario viene letto da un oggetto FileStream
  2. al momento della deserializzazione, il FX utilizza un costruttore privato della classe. Tale costruttore ha due parametri come il metodo GetObjectData visto ieri: un SerializationInfo e un StreamingContext. Useremo il primo per valorizzare i membri della classe che ci servono

Ecco il codice che ho scritto io per deserializzare da uno stream binario:

static public T DeserializeBinary<T>(string fileName) where T : new()
{
    FileStream rd = 
new FileStream(fileName, FileMode.Open);
    BinaryFormatter form = 
new BinaryFormatter();
    T obj = (T) form.Deserialize(rd);
    
return (obj);
}

Il FileStream accede al file in modalità FileMode.Open, mentre il BinaryFormatter fa il resto. Nella chiamata al metodo Deserialize, l'oggetto di tipo T viene creato sfruttando il costruttore privato di cui parlavo prima. Per semplicità, riporto qui sotto quello implementato per la classe Chapter che dispone solo della public property Title.

private Chapter(SerializationInfo info, StreamingContext context)
{
    _Title = info.GetString("Title");
}

Se avessi dovuto fare la stessa cosa per la classe Book, potrei usare lo stesso metodo DeserializeBinary<T> visto sopra, ma avrei dovuto creare il costruttore privato valorizzando tutte le property Title, Author, Pages e così via. Notare che nella deserializzazione binaria non abbiamo a disposizione il metodo CanDeserialize, che funziona solamente quando si ha a che fare con XML. Quindi, è opportuno usare un try...catch per intercettare tutte le Exceptions possibili durante questa fase.

powered by IMHO 1.2

Print | posted on giovedì 9 febbraio 2006 17:20 | Filed Under [ Esame 70-536 ]

Feedback

Gravatar

# re: [70-536, #13] Deserializzazione di una classe da uno stream XML o binario (con contorno di Generics)

"Quindi, è opportuno usare un try...catch per intercettare tutte le Exceptions possibili durante questa fase."

se ti riferisci al classico.

try
{
}
catch (System.Exception <ex>)
{
}

la tua affermazione non è assolutamente corretta.

"Microsoft Design Rule"
09/02/2006 17:23 | Alessio Marziali
Gravatar

# re: [70-536, #13] Deserializzazione di una classe da uno stream XML o binario (con contorno di Generics)

ecco la domanda che volevo fare anche io, ma che ho aspettato che qualcun altro facesse.
si sa che il try-catch è un po' pesantuccio, però è la prima cosa che farei per testare se tutto va a buon fine
09/02/2006 18:56 | Igor Damiani
Gravatar

# re: [70-536, #13] Deserializzazione di una classe da uno stream XML o binario (con contorno di Generics)

grazie Marco per il tuo comment.
Sei stato chiarissimo, e grazie anche per il tuo link (che però faccio fatica ad aprire).
è che Alessio ha scritto così, senza dare alcuna info utile per capire, tutto qua...
10/02/2006 11:30 | Igor Damiani
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET