- Serializzazione e deserializzazione XML di oggetti -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario conoscenze basiche di VB .Net
Serializzazione XML di una classe contenente la configurazione del programma

INTRODUZIONE ALLA SERIALIZZAZIONE XML
Cosa significa a cosa serve serializzare oggetti tramite XML.

In molte occasioni può sorgere la necessità di serializzare (su disco o su un qualsiasi altro flusso, ad esempio di rete) il contenuto di un oggetto cosicché possa essere caricato in seguito, magari da un altro software scritto in un altro linguaggio su un'altra piattaforma. Per fare questo esiste la possibilità di servirsi delle classi per la serializzazione binaria offerte dal framework .Net (in particolare System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) ma se è necessario che i dati siano consultabili da piattaforme diverse (ad esempio da un'applicazione in Java, JavaScript o qualunque altro linguaggio) o anche da un essere umano, ancora una volta, la scelta migliore è certamente servirsi di XML, formato elegante, standard ed estremamente portabile. La classe chiave per la serializzazione/de-serializzazione in XML è XmlSerializer in System.Xml.Serialization: grazie ai metodi Serialize e Deserialize in poche righe di codice è possibile creare un documento XML che rappresenta i campi pubblici (proprietà e membri) di un certo oggetto.
Introduciamo fin da subito alcune restrizioni dunque: la serializzazione ha luogo solamente su campi pubblici (le proprietà ReadOnly o WriteOnly vengono ignorate nel caso di tipi primitivi come String, Integer, Double...) di classi che siano dotate di un costruttore privo di parametri. Ma vediamo subito come funziona.

SERIALIZZARE UNA CLASSE IN MANIERA PERSONALIZZATA
Serializzare la configurazione di un programma su disco personalizzando l'output XML.

Immaginiamo di dover serializzare una classe di configurazione di un client FTP definita come segue:


Public Class Configuration

    Private strHost As String
    Private intPort As Integer

    'Le proprietà pubbliche sono oggetto della serializzazione
    Public Property Host() As String
        Get
            Return strHost
        End Get
        Set(ByVal value As String)
            strHost = value
        End Set
    End Property

    Public Property Port() As Integer
        Get
            Return intPort
        End Get
        Set(ByVal value As Integer)
            intPort = value
        End Set
    End Property

End Class

Come si può vedere essa è composta da due campi privati e due proprietà pubbliche, una stringa e un numero intero. Vediamo come potrebbe essere scritta la routine da usare per salvare le informazioni che un'istanza della classe Configuration contiene:


Sub Save(ByVal strFile As String, ByVal cfg As Configuration)
    Dim wrtFile As New IO.FileStream(strFile, IO.FileMode.Create)
    Dim xsrSerializer As New XmlSerializer(GetType(Configuration))
    xsrSerializer.Serialize(wrtFile, cfg)
    wrtFile.Close()
End Sub

Il codice è estremamente semplice: dati un percorso di file e l'istanza della classe Configuration da serializzare su disco si crea un FileStream per scrivere su disco, si crea un oggetto di tipo XmlSerializer (situato nel namespace System.Xml.Serialization) passando al costruttore il tipo dell'oggetto che si desidera serializzare, in questo caso Configuration, a questo punto si chiama il metodo Serialize con parametri lo stream e l'istanza della classe in questione e infine si chiude lo stream aperto. Vediamo un possibile risultato prodotto da questo codice:


<?xml version="1.0"?>
<Configuration 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Host>ftp.test.com</Host>
  <Port>21</Port>
</Configuration>

Il codice XML dovrebbe essere abbastanza autoesplicativo, ma in sintesi è stato creato un elemento radice chiamato "Configuration" (come la classe stessa) dotato di alcuni attributi con riferimenti agli standard XSI e XSD (non importa ora capire di cosa si tratti) e due sottoelementi "Host" e "Port" che contengono a loro volta le informazioni dell'oggetto serializzato.
A questo punto è lecito chiedersi se sia possibile personalizzare questo output, ebbene è possibile avere un controllo abbastanza avanzato su esso tramite l'uso di attributi nel codice sorgente della classe. Vediamo alcuni esempi.
Per esempio per cambiare il nome dell'elemento radice da "Configuration" a "configurazione" basta utilizzare l'attributo XmlRoot sulla definizione della classe come segue:


<XmlRoot("configurazione")> _
Public Class Configuration
[...]
End Class

Per personalizzare il nome di un elemento figlio, ad esempio cambiare da "Host" a "Server" il nome dell'elemento corrispondente alla proprietà Host si deve usare l'attributo XmlElement:


<XmlElement("Server")> _
Public Property Host() As String
[...]
End Property

Se invece si desidera ignorare una proprietà perché non adatta a essere serializzata o comunque inutile ci si deve servire dell'attributo XmlIgnore. Ad esempio pensiamo di aggiungere la proprietà Uri, che restituisce un Uri per accedere al server FTP. Poiché System.Uri non è dotato di un costruttore privo di parametri va escluso dalla serializzazione:


<XmlIgnore()> _
Public ReadOnly Property Uri() As Uri
    Get
        Return New Uri(String.Format("ftp://{0}:{1}{2}/", Me.Host, Me.Port))
    End Get
End Property

Infine vediamo come vengono trattati gli elementi non primitivi (quindi qualcosa di più articolato di una semplice stringa o un numero intero). Creiamo la classe UserPass contenente una coppia di stringhe per nome utente e password e associamola ad una proprietà della classe Configuration tramite una lista, cosicché sia possibile per avere diverse credenziali (coppia di nome utente e password) per accedere al nostro ipotetico server FTP.



Publi Class Configuration
    Private uspUsers As New List(Of UserPass)
    [...]
    Public ReadOnly Property Users() As List(Of UserPass)
        Get
            Return uspUsers
        End Get
    End Property
    [...]
End Class

Vediamo la classe UserPass:



Public Class UserPass
    Private strUser As String
    Private strPass As String

    Public Sub New()
    End Sub

    <XmlAttribute("username")> _
    Public Property User() As String
        [...]
    End Property

    <XmlAttribute("password")> _
    Public Property Pass() As String
        [...]
    End Property

End Class

Come si nota la classe è dotata di un costruttore senza parametri e due proprietà pubbliche per nome utente e password con attributo XmlAttribute. Quest'ultimo indica che le due proprietà dovranno essere serializzate come attributi XML dell'elemento padre (in questo caso UserPass) rispettivamente chiamati "username" e "password". Ecco un possibile output XML:



<?xml version="1.0"?>
<configurazione 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Server>ftp.test.com</Server>
  <Port>21</Port>
  <Users>
    <UserPass username="anonymous" password="email@address.com" />
    <UserPass username="Carl" password="carlitos" />
  </Users>
</configurazione>
DESERIALIZZARE UN FILE XML
Come caricare dal disco un file XML rappresentante un oggetto serializzato in precedenza.

Ovviamente la serializzazione XML non avrebbe alcuna utilità senza la possibilità di de-serializzare, ecco dunque una ruotine per de-serializzare la classe Configuration da un file XML:


Function LoadFromFile(ByVal strFile As String) As Configuration
    Dim rdr As New IO.FileStream(strFile, IO.FileMode.Open)
    Dim xsrDeserializer As New XmlSerializer(GetType(Configuration))
    Dim cfgResult As Configuration = _
        CType(xsrDeserializer.Deserialize(rdr), Configuration)
    rdr.Close()
    Return cfgResult
End Function

Il procedimento è simile alla serializzazione se non per il fatto che il file viene aperto in modalità Open e si chiama il metodo Deserialize, passando solamente lo stream.

 

<< INDIETRO by VeNoM00