Pagina personale di:
Carlo Vecchio
appunti di C#, R, SQL Server, ASP.NET, algoritmi, numeri
Vai ai contenuti

C# - Serializzazione e Deserializzazione

C#

Serializzazione e Deserializzazione in XML (Serialization and Deserialization)

  • La Serializzazione è un processo che trasforma un oggetto in un insieme di dati (come un file XML). La Deserializzazione è il processo opposto.
  • Quando un oggetto viene serializzato, ne diventa facile il trasporto per esempio attraverso Internet, utilizzando un Client e un Server.
  • Una volta ricevuti i dati, tramite la deserializzazione, possono essere facilmente trasformati in oggetti e poi opportunamente elaborati.
  • Negli esempi seguenti si fa uso dei seguenti namespace:

   using System.Xml;
   using System.Xml.Serialization;

Serializzazione: oggetto -> stringa
  • Esempio di serializzazione di un oggetto in una stringa.
  • Si definisce la classe seguente con tre proprietà pubbliche ed una proprietà privata (che non viene serializzata).

   public class AddressDetails
   {
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
       private string PoAddress { get; set; }   // Private: non è serializzato
   }

  • Per serializzare un oggetto di questa classe in una stringa, si può utilizzare il codice seguente.

   // Creazione di un oggetto.
   AddressDetails details = new AddressDetails();
   details.HouseNo = 20;
   details.StreetName = "Via Torino";
   details.City = "Roma";

   // Serializzazione.
   string ret = "";
   XmlSerializer xmls = new XmlSerializer(typeof(AddressDetails));
   using (StringWriter sw = new StringWriter())
   {
       using (XmlWriter xmlw = XmlWriter.Create(sw))
       {
           xmls.Serialize(xmlw, details);
       }
       ret = sw.ToString();
   }

   // Output.
   Console.WriteLine(ret);

  • Il risultato che si ottiene è una stringa XML, che una volta sistemata per renderla più leggibile è:

   <?xml version="1.0" encoding="utf-16"?>
   <AddressDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <HouseNo>20</HouseNo>
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </AddressDetails>

  • Anche se l’oggetto è complesso, la serializzazione è possibile con lo stesso codice.
  • Si abbiano le classi seguenti ‘PersonalDetail’ e ‘Address’. La prima di queste classi ha tra le sue proprietà, un oggetto della seconda classe.

   public class PersonalDetails
   {
       public string Name { get; set; }
       public int Age { get; set; }
       public Address Address;
   }

   public class Address
   {
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
   }

  • La serializzazione è molto simile a quella precedente.

   // Creazione di un oggetto.
   PersonalDetails p = new PersonalDetails();
   p.Name = "Carlo";
   p.Age = 20;
   p.Address = new Address();
   p.Address.HouseNo = 20;
   p.Address.StreetName = "Via Torino";
   p.Address.City = "Roma";

   // Serializzazione.
   string ret = "";
   XmlSerializer xmls = new XmlSerializer(typeof(PersonalDetails));
   using (StringWriter sw = new StringWriter())
   {
       using (XmlWriter xmlw = XmlWriter.Create(sw))
       {
           xmls.Serialize(xmlw, p);
       }
       ret = sw.ToString();
   }

   // Output.
   Console.WriteLine(ret);

  • La stringa ottenuta è:

   <?xml version="1.0" encoding="utf-16"?>
   <PersonalDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <Address>
       <HouseNo>20</HouseNo>
       <StreetName>Via Torino</StreetName>
       <City>Roma</City>
     </Address>
     <Name>Carlo</Name>
     <Age>20</Age>
   </PersonalDetails>

Serializzazione: oggetto -> file
  • Esempio di serializzazione di un oggetto in un file.
  • Si utilizzano le stesse classi del paragrafo precedente.
  • Serializzazione di un oggetto creato dalla classe ‘AddressDetails’.

   // Creazione di un oggetto.
   AddressDetails details = new AddressDetails();
   details.HouseNo = 20;
   details.StreetName = "Via Torino";
   details.City = "Roma";

   // Serializzazione.
   XmlSerializer xmls = new XmlSerializer(typeof(AddressDetails));
   using (TextWriter tw = new StreamWriter(@"C:\Temp\Xml.xml"))
   {
       xmls.Serialize(tw, details);
   }

  • Serializzazione di un oggetto creato dalle classi ‘PersonalDetails’ e ‘Address’.

   // Creazione di un oggetto.
   PersonalDetails p = new PersonalDetails();
   p.Name = "Carlo";
   p.Age = 20;
   p.Address = new Address();
   p.Address.StreetName = "Via Torino";
   p.Address.HouseNo = 20;
   p.Address.City = "Roma";

   // Serializzazione.
   XmlSerializer serializer = new XmlSerializer(typeof(PersonalDetails));
   using (TextWriter writer = new StreamWriter(@"C:\Temp\Xml.xml"))
   {
       serializer.Serialize(writer, p);
   }

  • In entrambi i casi i file generati sono del tutto uguali alle stringhe generate nel paragrafo precedente.

Attributi
  • La serializzazione presenta alcuni attributi:
    • XmlElement
    • XmlAttribute
    • XmlIgnore
    • XmlRoot

XmlElement
  • Permette di avere un Tag con un diverso nome rispetto al nome della Proprietà.
  • Esempio:

   public class AddressDetails
   {
       [XmlElement("Numero")]
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
   }

  • Serializzazione, si osservi il Tag ‘Numero’ al posto del Tag ‘HouseNo’:

   <?xml version="1.0" encoding="utf-8"?>
   <AddressDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <Numero>20</Numero>
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </AddressDetails>

XmlAttribute
  • La proprietà con questo attributo, diventa un attributo del Tag di livello superiore.
  • Esempio:

   public class AddressDetails
   {
       [XmlAttribute("Numero")]
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
   }

  • Serializzazione, si osservi la proprietà ‘Numero’ in ‘AddressDetails’:

   <?xml version="1.0" encoding="utf-8"?>
   <AddressDetails xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema" Numero="20">
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </AddressDetails>

XmlIgnore
  • La proprietà con questo attributo non viene serializzata.
  • Esempio:

   public class AddressDetails
   {
       [XmlIgnore]
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
   }

  • Serializzazione, si osservi che la proprietà ‘HouseNo’ non viene serializzata.

   <?xml version="1.0" encoding="utf-8"?>
   <AddressDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </AddressDetails>

XmlRoot
  • L’attributo permette di cambiare il nome all’elemento di root della serializzazione. Senza questo attributo, l’elemento di root è serializzato con il nome della classe (negli esempi precedenti è ‘AddressDetails’).
  • Esempio:

   [XmlRoot("NewRoot")]
   public class AddressDetails
   {
       public int HouseNo { get; set; }
       public string StreetName { get; set; }
       public string City { get; set; }
       private string PoAddress { get; set; }   // Private: non è serializzato
   }

  • Serializzazione, si osservi il nome dell’elemento di root che è ‘NewRoot’ e non ‘AddressDetails’:

   <?xml version="1.0" encoding="utf-8"?>
   <NewRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <HouseNo>20</HouseNo>
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </NewRoot>

Deserializzazione: stringa -> oggetto
  • Esempio di deserializzazione di una stringa in un oggetto.
  • Si fa riferimento alle stesse classi definite nei paragrafi precedenti.
  • La stringa da deserializzare è la seguente:

   <?xml version="1.0" encoding="utf-16"?>
   <AddressDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <HouseNo>20</HouseNo>
     <StreetName>Via Torino</StreetName>
     <City>Roma</City>
   </AddressDetails>

  • La stringa da deserializzare viene caricata in un array di byte. In questo esempio proviene da un file, ma potrebbe provenire da un’altra fonte (per esempio un processo di comunicazione).

   // Creazione di un oggetto.
   AddressDetails details = new AddressDetails();

   // Creazione di uno stream.
   Byte[] ba = File.ReadAllBytes(@"C:\Temp\Xml.xml");

   using (StreamReader sr = new StreamReader(new MemoryStream(ba)))
   {
       // Deserializzazione.
       XmlSerializer xmls = new XmlSerializer(typeof(AddressDetails));
       details = (AddressDetails)xmls.Deserialize(sr);
   }

  • L’oggetto ‘details’ dopo la deserializzazione, ha le proprietà valorizzate.

Deserializzazione: file -> oggetto
  • Esempio di deserializzazione di un file in un oggetto.
  • Il file da deserializzare viene elaborato direttamente senza il passaggio ad un array di byte.

   // Creazione di un oggetto.
   AddressDetails details = new AddressDetails();

   // Deserializzazione.
   XmlSerializer xmls = new XmlSerializer(typeof(AddressDetails));
   using (TextReader tr = new StreamReader(@"C:\Temp\Xml.xml"))
   {
       details = (AddressDetails)xmls.Deserialize(tr);
   }

Deserializzazioni complesse
  • I dati o il file da deserializzare può essere più complesso.
  • In questo esempio si deserializza questo file:

   <?xml version="1.0" encoding="utf-16"?>
   <AddressList>
     <Address>
       <HouseNo>10</HouseNo>
       <StreetName>Via Milano</StreetName>
       <City>Roma</City>
     </Address>
     <Address>
       <HouseNo>20</HouseNo>
       <StreetName>Via Torino</StreetName>
       <City>Venezia</City>
     </Address>
   </AddressList>

  • Questo file contiene un elenco di oggetti (in questo caso solo 2 oggetti).
  • La classe per l’oggetto interno ‘Address’ è già stata definita negli esempi precedenti, mentre occorre definire una classe per l’oggetto esterno, che è una lista di ‘Address’.
  • Quindi si definisce la classe:

   public class AddressList
   {
       [XmlElement("Address")]
       public List<Address> AddrList { get; set; }
   }

  • È importante impostare l’attributo ‘XmlElement’ per indicare che gli elementi interni sono ‘Address’.
  • La deserializzazione avviene con il codice simile a quello già visto:

   // Creazione di un oggetto.
   AddressList details = new AddressList();

   // Deserializzazione.
   XmlSerializer xmls = new XmlSerializer(typeof(AddressList));
   using (TextReader tr = new StreamReader(@"C:\Temp\Xml.xml"))
   {
       details = (AddressList)xmls.Deserialize(tr);
   }

  • Si è ottenuto l’oggetto ‘details’ che è una lista con due elementi di tipo ‘Address’.
  • Altro esempio: si condideri il file seguente.

   <?xml version="1.0" encoding="utf-16"?>
   <AddressList>
     <Company>MyCompany</Company>
     <Year>2016</Year>
     <Address>
       <HouseNo>10</HouseNo>
       <StreetName>Via Milano</StreetName>
       <City>Roma</City>
     </Address>
     <Address>
       <HouseNo>20</HouseNo>
       <StreetName>Via Torino</StreetName>
       <City>Venezia</City>
     </Address>
     </AddressList>

  • Questo file è come il precedente con l’aggiunta di altri due Tag.
  • Per deserializzarlo alla classe ‘AddressList’ occorre aggiungere altre due proprietà:

   public class AddressList
   {
       public string Company { get; set; }
       public int Year { get; set; }
       [XmlElement("Address")]
       public List<Address> AddrList { get; set; }
   }

  • Il codice per deserializzare il file è identico al caso precedente.

Lettura nodo singolo
  • È possibile leggere nodi singoli di un file o uno stream Xml.
  • Per questi esempi si consideri il file Xml seguente (si noti la codifica UTF-8):

   <?xml version=”1.0” encoding=”utf-8”?>
   <AddressList>
     <Company>MyCompany</Company>
     <Year>2016</Year>
     <Address>
       <HouseNo>10</HouseNo>
       <StreetName>Via Milano</StreetName>
       <City>Roma</City>
     </Address>
     <Address>
       <HouseNo>20</HouseNo>
       <StreetName>Via Torino</StreetName>
       <City>Venezia</City>
     </Address>
   </AddressList>

  • Lettura singolo nodo da file:

   XmlDocument xmld = new XmlDocument();
   xmld.Load(@"C:\Temp\Xml.xml");
   Console.WriteLine(xmld.SelectSingleNode("AddressList/Company").InnerText);

  • Lettura singolo nodo da stream:

   XmlDocument xmld = new XmlDocument();
   Byte[] ba = File.ReadAllBytes(@"C:\Temp\Xml.xml");
   xmld.Load(new MemoryStream(ba));
   Console.WriteLine(xmld.SelectSingleNode("AddressList/Company").InnerText);

  • Lettura singolo nodo da URL (impostare un URL valido!):

   XmlDocument xmld = new XmlDocument();
   xmld.Load("http://sitoweb.com/file.xml");
   Console.WriteLine(xmld.SelectSingleNode("AddressList/Company").InnerText);

  • Si noti che se ci sono più nodi con lo stesso ‘xmlpath’ (per esempio i nodi ‘AddressList/Address/City’), il metodo ‘SelectSingleNode’ restituisce il primo di questi.

Lettura nodi multipli
  • È possibile leggere tutti i nodi che hanno lo stesso ‘xmlpath’.
  • Esempio di lettura da file (analoga procedura per la lettura da stream e da URL):

   XmlDocument xmld = new XmlDocument();
   xmld.Load(@"C:\Temp\Xml.xml");
   foreach (XmlNode node in xmld.SelectNodes("AddressList/Address"))
   {
       Console.WriteLine(node.SelectSingleNode("City").InnerText);
   }

Modifica nodi
  • Per modificare il valore di un nodo, occorre impostare la proprietà ‘InnerText’ e poi eseguire il metodo ‘Save’:

   XmlDocument xmld = new XmlDocument();
   xmld.Load(@"C:\Temp\Xml.xml");
   Console.WriteLine("Prima: " + xmld.SelectSingleNode("AddressList/Company").InnerText);
   xmld.SelectSingleNode("AddressList/Company").InnerText = "NewCompany";
   xmld.Save(@"C:\Temp\Xml.xml");
   Console.WriteLine("Dopo: " + xmld.SelectSingleNode("AddressList/Company").InnerText);

Scrittura nuovo file XML
  • Per scrivere un file Xml, si può utilizzare la classe ‘XmlTextWriter’; ecco un semplice esempio:

   XmlTextWriter xmlt = new XmlTextWriter(@"C:\Temp\Xml.xml", Encoding.UTF8);
   xmlt.Formatting = Formatting.Indented;
   xmlt.WriteStartElement("CompanyProperties");
   xmlt.WriteStartElement("CompanyName");
   xmlt.WriteString("MyCompany");
   xmlt.WriteEndElement();   // CompanyProperties
   xmlt.WriteStartElement("Year");
   xmlt.WriteString("2016");
   xmlt.WriteEndElement();   // Year
  xmlt.WriteEndElement();   // CompanyProperties
   xmlt.Close();

  • Il file prodotto è il seguente:

   <CompanyProperties>
   <CompanyName>MyCompany</CompanyName>
   <Year>2016</Year>
   </CompanyProperties>

Aggiunta di un nodo ad un file XML
  • Per aggiungere ad un file esistente, un nodo con le proprietà, utilizzare la classe ‘XmlNode’; ecco un esempio:

   XmlDocument xmld = new XmlDocument();
   xmld.Load(@"C:\Temp\Xml.xml");
   XmlNode person = xmld.CreateElement("Person");
   XmlNode firstName = xmld.CreateElement("FirstName");
   firstName.InnerText = "Leonardo";
   XmlNode lastName = xmld.CreateElement("LastName");
   lastName.InnerText = "Da Vinci";
   person.AppendChild(firstName);
   person.AppendChild(lastName);
   xmld.DocumentElement.AppendChild(person);
   xmld.Save(@"C:\Temp\Xml.xml");

  • Al file viene aggiunto il seguente nodo:

    <Person>
      <FirstName>Leonardo</FirstName>
      <LastName>Da Vinci</LastName>
    </Person>

Rimozione di un nodo da un file XML
  • Per rimuovere ad un file esistente, un nodo la cui proprietà (una qualsiasi) sia uguale ad un certo valore, utilizzare la classe ‘XmlNode’; ecco un esempio:

   XmlDocument xmld = new XmlDocument();
   xmld.Load(@"C:\Temp\Xml.xml");
   foreach (XmlNode node in xmld.SelectNodes("CompanyProperties/Person"))
   {
       if (node.SelectSingleNode("FirstName").InnerText == "Leonardo")
       {
           node.ParentNode.RemoveChild(node);
       }
   }

   xmld.Save(@"C:\Temp\Xml.xml");

  • File Xml prima dell’eliminazione del nodo:

   <CompanyProperties>
     <CompanyName>MyCompany</CompanyName>
     <Year>2016</Year>
     <Person>
       <FirstName>Leonardo</FirstName>
       <LastName>Da Vinci</LastName>
     </Person>
   </CompanyProperties>

  • File Xml dopo l’eliminazione del nodo:

   <CompanyProperties>
     <CompanyName>MyCompany</CompanyName>
     <Year>2016</Year>
   </CompanyProperties>

 
© 2022 Carlo Vecchio
Torna ai contenuti