mercoledì 5 gennaio 2011

VB for Dummies: La serializzazione – parte 1

Questo post è dedicato alla serializzazione e a come sia possibile sfruttarla in Visual Basic.

Partiamo con una definizione di serializzazione presa da Wikipedia:

In informatica, la serializzazione è un processo per salvare un oggetto in un supporto di memorizzazione lineare (ad esempio, un file o un'area di memoria), o per trasmetterlo su una connessione di rete. La serializzazione può essere in forma binaria o può utilizzare codifiche testuali (ad esempio il formato XML) direttamente leggibili dagli esseri umani. Lo scopo della serializzazione è di trasmettere l'intero stato dell'oggetto in modo che esso possa essere successivamente ricreato nello stesso identico stato dal processo inverso, chiamato deserializzazione.

In sostanza si tratta di poter “scrivere” l’oggetto in un formato standard per poter essere “ricostruito” in un momento successivo, in un’altra applicazione o, addirittura in altri linguaggi e piattaforme di sviluppo.

In questo post tralasceremo la serializzazione binaria e ci occuperemo delle tre principali modalità di serializzazione testuali utilizzate: XML, SOAP e JSON.

Poichè serializzare significa prendere un nostro oggetto e “trasformarlo” in una stringa (in parole veramente molto semplici) con un determinato formato, dobbiamo partire da un oggetto e per fare questo creiamo una classe fattura (molto semplice!!) che utilizzeremo nei nostri esperimenti:

image

Le classi in gioco sono, evidentemente molto semplici e servono esclusivamente da esempio senza avere la pretesa di essere esaustive.

La proprietà Totale della classe Fattura è di tipo ReadOnly (così come Totale e Totale Ivato del Dettaglio Fattura) e vedremo come questo influenza la serializzazione.

In dettagli le classi sono le seguenti:

  1. Imports System.Xml.Serialization
  2.  
  3. Public Class Fattura
  4.     Public Sub New()
  5.         Dettagli = New List(Of DettaglioFattura)
  6.     End Sub
  7.  
  8.     Public Property DataEmissione As DateTime
  9.     Public Property Cliente As Cliente
  10.     Public Property NumeroDocumento As String
  11.     Public Property Dettagli As List(Of DettaglioFattura)
  12.     Public Property Stato As StatoFattura
  13.  
  14.     Public ReadOnly Property Totale As Decimal
  15.         Get
  16.             If Dettagli Is Nothing Then
  17.                 Return 0
  18.             Else
  19.                 Return Dettagli.Sum(Function(d) d.TotaleIvato)
  20.             End If
  21.         End Get
  22.     End Property
  23.  
  24. End Class

 

  1. Public Class DettaglioFattura
  2.  
  3.     Public Property Descrizione As String
  4.     Public Property Codice As String
  5.     Public Property Quantita As Integer
  6.     Public Property PrezzoUnitario As Decimal
  7.     Public Property Iva As Decimal
  8.  
  9.     Public ReadOnly Property Totale As Decimal
  10.         Get
  11.             Return Quantita * PrezzoUnitario
  12.         End Get
  13.     End Property
  14.  
  15.     Public ReadOnly Property TotaleIvato As Decimal
  16.         Get
  17.             Return Totale * (1 + Iva)
  18.         End Get
  19.     End Property
  20. End Class

 

  1. Public Class Cliente
  2.  
  3.     Public Property Denominazione As String
  4.     Public Property CodiceFiscale As String
  5.     Public Property PartitaIVA As String
  6.  
  7. End Class

 

  1. Public Enum StatoFattura
  2.     DaPagare
  3.     Pagata
  4.     Annullata
  5. End Enum

 

Serializzazione in XML

Il primo tipo di serializzazione che vedremo è quello XML, cioè vedremo come “scrivere” le istanze della nostra classe fattura in formato XML.

Per poter serializzare una nostra classe in formato XML, la classe stessa deve essere serializzabile, ovvero composta da proprietà (i metodi non vengono serializzati) serializzabili. Se, ad esempio la nostra classe contiene una proprietà di un tipo .NET non serializzabile l’intera classe non è serializzabile.

Il processo di serializzazione si basa sull’utilizzo della classe XmlSerializer e di un oggetto che serva da flusso in cui scrivere l’XML risultante (un XmlWriter, un TextWriter, uno Stream).

In particolare il costruttore dell’XmlSerializer prevede che venga dichiarata per quale classe stiamo costruendo il serializzatore, quindi possiamo utilizzare il metodo Serialize() per scrivere l’XML risultatnte nello stream o nel writer opportuno. La seguente funzione restituisce la stringa XML utilizzando uno StringWriter:

  1. Public Shared Function SerializzaXML(ByVal fattura As Fattura) As String
  2.     If fattura Is Nothing Then Throw New ArgumentNullException("Fattura")
  3.     Dim strXml As String = Nothing
  4.     Dim writer As New XmlSerializer(GetType(Fattura))
  5.     Using strWriter As New StringWriter()
  6.         writer.Serialize(strWriter, fattura)
  7.         strXml = strWriter.ToString()
  8.     End Using
  9.     Return strXml
  10. End Function

La seguente figura mostra la serializzazione di una fattura di prova:

image

Come possiamo osservare, tutte le proprietà della nostra classe vengono riportate all’interno dell’XML frutto della serializzazione. Ogni proprietà diventa un tag XML con il nome pari al nome della proprietà. Infine lo stato della fattura (definito come un’enumerazione) viene scritto come una stringa esattamente pari al valore dell’enumerazione impostato. A livello di codice, nel momento in cui viene eseguito il Serialize, vengono richiamati tutti i Get delle proprietà lettura/scrittura della classe (nel caso precedente non la proprietà Totale).

Di fatto, il comportamento visto in precedenza, è quello di default ma abbiamo la possibilità di intervenire per modificare il risultato dell’XML e lo possiamo fare utilizzando degli appositi attributi copntenuti nel namespace System.Xml.Serialization.

Se vogliamo, ad esempio, che una o più proprietà della nostra classe non finiscano all’interno dell’XML, possiamo utilizzare l’attributo XmlIgnoreAttribute decorando proprio la proprietà (o l’attributo) che non vogliamo finisca all’interno dell’XML.

Ad esempio, supponiamo di avere una proprietà della nostra fattura, chiamata Id, che non vogliamo serializzare. Potremo scrivere:

  1. <XmlIgnore()>
  2. Public Property Id As Integer?

In questo modo l’XML che si ottiene non ha il tag <Id>.

In maniera analoga, se vogliamo che una nostra proprietà non diventi un tag XML ma un attributo del tag Fattura, possiamo utilizzare l’attributo XmlAttribute indicando, eventualmente, il nome dell’attributo (altrimenti viene utilizzato il nome della proprietà):

  1. <XmlAttribute()>
  2. Public Property NumeroDocumento As String

E se volessimo che un tag XML non abbia il nome della proprietà ma un altro nome, allora potremmo utilizzare l’attributo XmlElement indicamndo il nome del tag:

  1. <XmlElement("Emissione")>
  2. Public Property DataEmissione As DateTime

Gli attributi contenuti nel namespace System.Serialization ci consentono, dunque di modificare il risultato della serializzazione a partire da quello di default.

Tutto ciò è utilissimo, non tanto quando siamo noi a gestire il gioco e definire lo schema dell’XML ma quando ci viene assegnato uno schema prestabilito e vogliamo creare un mapping a noi comodo con una classe da noi creata.

Vediamo, ora, la deserializzazione, ovvero il processo inverso della serializzazione, cioè quella procedura che ci permette di creare un oggetto a partire dall’XML.

L’XmlSerializer ci consente di deserializzare una stringa XML in un oggetto. In questo caso, anzichè un writer come supporto, dobbiamo utilizzare un reader:

  1. Public Shared Function DeserializzaXML(ByVal strFattura As String) As Fattura
  2.     Dim retObj As Fattura = Nothing
  3.     Dim reader = New XmlSerializer(GetType(Fattura))
  4.     Dim strReader = New StringReader(strFattura)
  5.     Try
  6.         retObj = CType(reader.Deserialize(strReader), Fattura)
  7.     Catch ex As Exception
  8.         Throw
  9.     End Try
  10.     Return retObj
  11. End Function

Ovviamente, se l’XML non è aderente allo schema, otteniamo un’eccezione nel momento in cui deserializziamo.

Ma cosa succede quando viene eseguite la deserializzazione?

Nel momento in cui viene richiamato il metodo Deserialize, viene istanziato l’oggetto di classe Fattiura (viene chiamato il costruttore) e, quindi, in sequenza vengono richiamati i set delle proprietà trovate all’interno dell’XML.

Per questo motivo, se non inseriamo un tag XML all’interno del file serializzato, la proprietà risultante conterrà il valore di default. Attenzione, quindi, a che le proprietà abbiano un valore di default coerente.

Nel prossimo post vedremo brevemente la serializzazione SOAP e, soprattutto, la serializzazione JSON.

Stay Tuned!!!!


 

Nessun commento: