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:
1 static void Main(string[] args)
2 {
3 Student student = new Student("Andrea", "Angella", new DateTime(1984, 7, 14));
4
5 // Binary Serialization
6 using (FileStream fs = new FileStream("file.dat", FileMode.Create))
7 {
8 BinaryFormatter bf = new BinaryFormatter();
9 bf.Serialize(fs, student);
10 }
11
12 // Binary Deserialization
13 using (FileStream fs = new FileStream("file.dat", FileMode.Open))
14 {
15 BinaryFormatter bf = new BinaryFormatter();
16 student = (Student)bf.Deserialize(fs);
17 }
18
19 Console.Write(student);
20 Console.ReadKey();
21 }
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. ).
1 [Serializable]
2 public class Student : ISerializable
3 {
4 public string Name { get; set; }
5 public string Surname { get; set; }
6 public DateTime BirthDate { get; set; }
7 public int Age { get; set; }
8
9 // Costruttore standard
10 public Student(string name, string surname, DateTime birthDate)
11 {
12 Name = name;
13 Surname = surname;
14 BirthDate = birthDate;
15 calculateAge();
16 }
17
18 // Costruttore per la deserializzazione
19 public Student(SerializationInfo info, StreamingContext context)
20 {
21 Name = info.GetString("Name");
22 Surname = info.GetString("Surname");
23 BirthDate = info.GetDateTime("BirthDate");
24 }
25
26 [OnSerializing]
27 void BeforeSerialization(StreamingContext context)
28 {
29 // ...
30 }
31
32 [OnSerialized]
33 void AfterSerialization(StreamingContext context)
34 {
35 // ...
36 }
37
38 [OnDeserializing]
39 void BeforeDeserialization(StreamingContext context)
40 {
41 // ...
42 }
43
44 [OnDeserialized]
45 void AfterDeserialization(StreamingContext context)
46 {
47 calculateAge();
48 }
49
50 public override string ToString()
51 {
52 return Name + ", " + Surname + ", " + BirthDate.ToShortDateString() + ", " + Age;
53 }
54
55 private void calculateAge()
56 {
57 Age = DateTime.Now.Year - BirthDate.Year;
58 }
59
60 #region ISerializable Members
61
62 // Metodo chiamato durante la fase di serializzazione
63 public void GetObjectData(SerializationInfo info, StreamingContext context)
64 {
65 info.AddValue("Name", Name);
66 info.AddValue("Surname", Surname);
67 info.AddValue("BirthDate", BirthDate);
68 }
69
70 #endregion
71 }
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.