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:
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:
- Imports System.Xml.Serialization
- Public Class Fattura
- Public Sub New()
- Dettagli = New List(Of DettaglioFattura)
- End Sub
- Public Property DataEmissione As DateTime
- Public Property Cliente As Cliente
- Public Property NumeroDocumento As String
- Public Property Dettagli As List(Of DettaglioFattura)
- Public Property Stato As StatoFattura
- Public ReadOnly Property Totale As Decimal
- Get
- If Dettagli Is Nothing Then
- Return 0
- Else
- Return Dettagli.Sum(Function(d) d.TotaleIvato)
- End If
- End Get
- End Property
- End Class
- Public Class DettaglioFattura
- Public Property Descrizione As String
- Public Property Codice As String
- Public Property Quantita As Integer
- Public Property PrezzoUnitario As Decimal
- Public Property Iva As Decimal
- Public ReadOnly Property Totale As Decimal
- Get
- Return Quantita * PrezzoUnitario
- End Get
- End Property
- Public ReadOnly Property TotaleIvato As Decimal
- Get
- Return Totale * (1 + Iva)
- End Get
- End Property
- End Class
- Public Class Cliente
- Public Property Denominazione As String
- Public Property CodiceFiscale As String
- Public Property PartitaIVA As String
- End Class
- Public Enum StatoFattura
- DaPagare
- Pagata
- Annullata
- 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:
- Public Shared Function SerializzaXML(ByVal fattura As Fattura) As String
- If fattura Is Nothing Then Throw New ArgumentNullException("Fattura")
- Dim strXml As String = Nothing
- Dim writer As New XmlSerializer(GetType(Fattura))
- Using strWriter As New StringWriter()
- writer.Serialize(strWriter, fattura)
- strXml = strWriter.ToString()
- End Using
- Return strXml
- End Function
La seguente figura mostra la serializzazione di una fattura di prova:
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:
- <XmlIgnore()>
- 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à):
- <XmlAttribute()>
- 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:
- <XmlElement("Emissione")>
- 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:
- Public Shared Function DeserializzaXML(ByVal strFattura As String) As Fattura
- Dim retObj As Fattura = Nothing
- Dim reader = New XmlSerializer(GetType(Fattura))
- Dim strReader = New StringReader(strFattura)
- Try
- retObj = CType(reader.Deserialize(strReader), Fattura)
- Catch ex As Exception
- Throw
- End Try
- Return retObj
- 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!!!!
Commenti