Area di riferimento
- Implementing serialization and input/output functionality in a .NET Framework application (18 percent)
- Serialize or deserialize an object or an object graph by using runtime serialization techniques.
- May include but is not limited to: Serialization interfaces; Serialization attributes; SerializationEntry structure and SerializationInfo class;
- ObjectManager class; Formatter class, FormatterConverter class, and FormatterServices class; StreamingContext structure
- Control the serialization of an object into XML format by using the System.Xml.Serialization namespace.
- May include but is not limited to: Serialize and deserialize objects into XML format by using the XmlSerializer class; Control serialization by using serialization attributes;
- Implement XML serialization interfaces to provide custom formatting for XML serialization; Delegates and event handlers provided by the System.Xml.Serialization namespace
- Implement custom serialization formatting by using the Serialization Formatter classes.
- May include but is not limited to: SoapFormatter; BinaryFormatter class
Serialization and Deserialization
La serializzazione consiste nel trasformare un oggetto in una sequenza di byte che può essere memorizzata o trasferita tra diverse applicazioni.
Affinchè un tipo possa essere serializzato è necessario decorarlo con l'attributo [Serializable]. Se non non si vuole serializzare un particolare membro allora si utilizza l'attributo [NonSerialized]. Nel caso fosse necessario eseguire del codice dopo l'operazione di deserializzazione (per esempio per impostare membri calcolati che non sono stati serializzati) si può implementare l'interfaccia IDeserializationCallback come mostrato nell'esempio.
1 [Serializable]
2 class Student : IDeserializationCallback
3 {
4 public Student(string name, string surname, DateTime birthDate)
5 {
6 Name = name;
7 Surname = surname;
8 BirthDate = birthDate;
9 calculateAge();
10 }
11
12 public string Name { get; set; }
13 public string Surname { get; set; }
14 public DateTime BirthDate { get; set; }
15
16 [NonSerialized]
17 [SoapIgnore]
18 private int age;
19
20 public int Age
21 {
22 get
23 {
24 return age;
25 }
26 set
27 {
28 age = value;
29 }
30 }
31
32 public override string ToString()
33 {
34 return Name + ", " + Surname + ", " + BirthDate.ToShortDateString() + ", " + age;
35 }
36
37 private void calculateAge()
38 {
39 Age = DateTime.Now.Year - BirthDate.Year;
40 }
41
42 public void OnDeserialization(object sender)
43 {
44 calculateAge();
45 }
46 }
Con la classe BinaryFormatter è possibile serializzare in modo efficiente l'oggetto ma questo sarà accessibile solo da parte di altre applicazioni .NET:
static void Main(string[] args)
{
Student student = new Student("Andrea", "Angella", new DateTime(1984, 7, 14));
// Binary Serialization
using (FileStream fs = new FileStream("file.dat", FileMode.Create))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, student);
}
// Binary Deserialization
using (FileStream fs = new FileStream("file.dat", FileMode.Open))
{
BinaryFormatter bf = new BinaryFormatter();
student = (Student)bf.Deserialize(fs);
}
Console.Write(student);
Console.ReadKey();
}
La classe SoapFormatter (assembly System.Runtime.Serialization.Formatters.Soap) permette di serializzare un oggetto come un SOAP Envelop adatto per essere condiviso anche con applicazioni non appartenenti al mondo .NET. Il suo utilizzo è analogo a quello della classe BinaryFormatter. E' possibile controllare la formattazione utilizzando opportuni attributi come [SoapAttribute], [SoapElement], [SoapEnum], [SoapIgnore] e [SoapInclude].
E' possibile serializzare un oggetto in uno specifico formato XML attraverso la classe XmlSerializer. Questa modalità ovviamente porta con se i vantaggi del linguaggio XML e cioè forte interoperabilità, amministrazione user-friendly e migliore compatibilità all'indietro. La serializzazione XML può serializzare solamente membri pubblici (al contrario di quella binaria) e non possono essere serializzati grafi di oggetti. E' richiesto un costruttore di default. Anche in questo caso è possibile personalizzare il markup xml che sarà generato utilizzando opportuni attributi come [XmlAttribute], [XmlElement], [XmlIgnore], [XmlEnum] ecc.
E' interessante la possibiiltà di generare classi con attributi xml a partire da un file XML Schema Definition. Lo strumento da utilizzare è il tool a linea di comando xsd.
Se si vuole avere un controllo maggiore sul processo di serializzazione e deserializzazione è possibile implementare l'interfaccia ISerializable. Tramite il metodo GetObjectData si specificano i membri che si vogliono serializzare. All'interno di un particolare costruttore (chiamato dopo la fase di deserializzazione) sarà possibile accedere a questi valori e ripristinare il contenuto dell'oggetto. E' possibile intervenire prima e dopo la fase di serializzazione e deserializzazione inserendo opportuni metodi decorati con gli attributi [OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized]. La classe StreamingContext è utile se si vuole serializzare un oggetto in modo differente in base alla destinazione (stesso processo, processi su macchine differenti, ecc. ).
[Serializable]
public class Student : ISerializable
{
public string Name { get; set; }
public string Surname { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
// Costruttore standard
public Student(string name, string surname, DateTime birthDate)
{
Name = name;
Surname = surname;
BirthDate = birthDate;
calculateAge();
}
// Costruttore per la deserializzazione
public Student(SerializationInfo info, StreamingContext context)
{
Name = info.GetString("Name");
Surname = info.GetString("Surname");
BirthDate = info.GetDateTime("BirthDate");
}
[OnSerializing]
void BeforeSerialization(StreamingContext context)
{
// ...
}
[OnSerialized]
void AfterSerialization(StreamingContext context)
{
// ...
}
[OnDeserializing]
void BeforeDeserialization(StreamingContext context)
{
// ...
}
[OnDeserialized]
void AfterDeserialization(StreamingContext context)
{
calculateAge();
}
public override string ToString()
{
return Name + ", " + Surname + ", " + BirthDate.ToShortDateString() + ", " + Age;
}
private void calculateAge()
{
Age = DateTime.Now.Year - BirthDate.Year;
}
#region ISerializable Members
// Metodo chiamato durante la fase di serializzazione
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", Name);
info.AddValue("Surname", Surname);
info.AddValue("BirthDate", BirthDate);
}
#endregion
}
Anche se è raramente necessario è bene sapere che è offerta anche la possibilità di realizzare dei formatter personalizzati. Invece di utilizzare ad esempio la classe BinaryFormatter è possibile costruire il proprio formatter implementando l'interfaccia IFormatter o IGenericFormatter.