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>