Code Listings

Chapter 15: Serialization

Basic use of DataContractSerializer:

namespace SerialTest
{
  [DataContract] public class Person
  {
    [DataMember] public string Name;
    [DataMember] public int Age;
  }
}
Person p = new Person { Name = "Stacey", Age = 30 };

DataContractSerializer ds = new DataContractSerializer (typeof (Person));

using (Stream s = File.Create ("person.xml"))
  ds.WriteObject (s, p);                            // Serialize

Person p2;
using (Stream s = File.OpenRead ("person.xml"))
  p2 = (Person) ds.ReadObject (s);                  // Deserialize

Console.WriteLine (p2.Name + " " + p2.Age);         // Stacey 30

Formatting the output:

Person p = new Person { Name = "Stacey", Age = 30 };
DataContractSerializer ds = new DataContractSerializer (typeof (Person));

XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
using (XmlWriter w = XmlWriter.Create ("person.xml", settings))
  ds.WriteObject (w, p);

System.Diagnostics.Process.Start ("person.xml");

Overriding data member names:

[DataContract (Name="Candidate", Namespace="http://oreilly.com/nutshell")]
public class Person
{
  [DataMember (Name="FirstName")]  public string Name;
  [DataMember (Name="ClaimedAge")] public int Age;
}

Specifying a binary formatter with the data contract serializer:

Person p = new Person { Name = "Stacey", Age = 30 };
DataContractSerializer ds = new DataContractSerializer (typeof (Person));

MemoryStream s = new MemoryStream();
using (XmlDictionaryWriter w = XmlDictionaryWriter.CreateBinaryWriter (s))
  ds.WriteObject (w, p);

MemoryStream s2 = new MemoryStream (s.ToArray());
Person p2;
using (XmlDictionaryReader r = XmlDictionaryReader.CreateBinaryReader (s2,
                               XmlDictionaryReaderQuotas.Max))
  p2 = (Person) ds.ReadObject (r);

Serializing subclasses:

[DataContract, KnownType (typeof (Student)), KnownType (typeof (Teacher))]
public class Person
{
  [DataMember] public string Name;
  [DataMember] public int Age;
}

[DataContract] public class Student : Person { }
[DataContract] public class Teacher : Person { }
static Person DeepClone (Person p)
{
  DataContractSerializer ds = new DataContractSerializer (typeof (Person));
  MemoryStream stream = new MemoryStream();
  ds.WriteObject (stream, p);
  stream.Position = 0;
  return (Person) ds.ReadObject (stream);
}
Person  person  = new Person  { Name = "Stacey", Age = 30 };
Student student = new Student { Name = "Stacey", Age = 30 };
Teacher teacher = new Teacher { Name = "Stacey", Age = 30 };

Person  p2 =           DeepClone (person);
Student s2 = (Student) DeepClone (student);
Teacher t2 = (Teacher) DeepClone (teacher);

Alternative to KnownType on [DataContract]:

DataContractSerializer ds = new DataContractSerializer (typeof (Person),
  new Type[] { typeof (Student), typeof (Teacher) } );

Object references:

[DataContract] public class Person
{
  [DataMember] public string Name;
  [DataMember] public int Age;
  [DataMember] public Address HomeAddress;
}

[DataContract, KnownType (typeof (USAddress))]
public class Address
{
  [DataMember] public string Street, Postcode;
}

Version tolerance:

[DataContract] public class Person : IExtensibleDataObject
{
  [DataMember] public string Name;
  [DataMember] public int Age;

  ExtensionDataObject IExtensibleDataObject.ExtensionData { get; set; }
}

Member ordering:

[DataContract] public class Person
{
  [DataMember (Order=0)] public string Name;
  [DataMember (Order=1)] public int Age;
}

Null and empty values:

[DataContract] public class Person
{
  [DataMember (EmitDefaultValue=false)] public string Name;
  [DataMember (EmitDefaultValue=false)] public int Age;
}

Data contracts and collections:

[DataContract] public class Person
{
  [DataMember (Name="Addresses")]
  List<Address> _addresses;
  public IList<Address> Addresses { get { return _addresses; } }
}

[DataContract] public class Address
{
  [DataMember] public string Street, Postcode;
}

Subclassing collection elements:

[DataContract, KnownType (typeof (USAddress))]
public class Address
{
  [DataMember] public string Street, Postcode;
}

public class USAddress : Address { }

Customizing collection and element names:

[CollectionDataContract (ItemName="Residence")]
public class AddressList : Collection<Address> { }

[DataContract] public class Person
{
  ...
  [DataMember] public AddressList Addresses;
}
[CollectionDataContract (ItemName="Entry",
                          KeyName="Kind",
                        ValueName="Number")]
public class PhoneNumberList : Dictionary <string, string> { }

[DataContract] public class Person
{
  ...
  [DataMember] public PhoneNumberList PhoneNumbers;
}

Serialization and deserialization hooks:

public DateTime DateOfBirth;

[DataMember] public bool Confidential;

[DataMember (Name="DateOfBirth", EmitDefaultValue=false)]
DateTime? _tempDateOfBirth;

[OnSerializing]
void PrepareForSerialization (StreamingContext sc)
{
  if (Confidential)
    _tempDateOfBirth = DateOfBirth;
  else
    _tempDateOfBirth = null;
}

Getting started with the binary engine:

[Serializable] public sealed class Person
{
  public string Name;
  public int Age;
}
Person p = new Person() { Name = "George", Age = 25 };

IFormatter formatter = new BinaryFormatter();

using (FileStream s = File.Create ("serialized.bin"))
  formatter.Serialize (s, p);

using (FileStream s = File.OpenRead ("serialized.bin"))
{
  Person p2 = (Person) formatter.Deserialize (s);
  Console.WriteLine (p2.Name + " " + p.Age);     // George 25
}

[NonSerialized]:

[Serializable] public sealed class Person
{
  public string Name;
  public DateTime DateOfBirth;

  // Age can be calculated, so there's no need to serialize it.
  [NonSerialized] public int Age;  
}

[OnDeserializing] and [OnDeserialized]

public sealed class Person
{
  public string Name;
  public DateTime DateOfBirth;

  [NonSerialized] public int Age;
  [NonSerialized] public bool Valid = true;

  public Person() { Valid = true; }
}

[OnSerializing] and [OnSerialized]

[Serializable] public sealed class Team
{
  public string Name;  
  Person[] _playersToSerialize;

  [NonSerialized] public List<Person> Players = new List<Person>();

  [OnSerializing]
  void OnSerializing (StreamingContext context)
  {
    _playersToSerialize = Players.ToArray();
  }

  [OnSerialized]
  void OnSerialized (StreamingContext context)
  {
    _playersToSerialize = null;   // Allow it to be freed from memory
  }

  [OnDeserialized]
  void OnDeserialized (StreamingContext context)
  {
    Players = new List<Person> (_playersToSerialize);
  }
}

[OptionalField] and versioning:

[Serializable] public sealed class Person       // Version 2 Robust
{
  public string Name;
  [OptionalField (VersionAdded = 2)] public DateTime DateOfBirth;
}

ISerializable:

[Serializable] public class Team : ISerializable
{
  public string Name;
  public List<Person> Players;

  public virtual void GetObjectData (SerializationInfo si,
                                     StreamingContext sc)
  {
    si.AddValue ("Name", Name);
    si.AddValue ("PlayerData", Players.ToArray());
  }

  public Team() {}
  
  protected Team (SerializationInfo si, StreamingContext sc)
  {
    Name = si.GetString ("Name");

    // Deserialize Players to an array to match our serialization:
    Person[] a = (Person[]) si.GetValue ("PlayerData", typeof (Person[]));

    // Construct a new List using this array:
    Players = new List<Person> (a);
  }
}

Getting started with XmlSerializer:

public class Person
{
  public string Name;
  public int Age;
}
p.Name = "Stacey"; p.Age = 30;

XmlSerializer xs = new XmlSerializer (typeof (Person));

using (Stream s = File.Create ("person.xml"))
  xs.Serialize (s, p);

Person p2;
using (Stream s = File.OpenRead ("person.xml"))
  p2 = (Person) xs.Deserialize (s);

Console.WriteLine (p2.Name + " " + p2.Age);   // Stacey 30

Attributes, names and namespaces:

public class Person
{
  [XmlElement ("FirstName")] public string Name;
  [XmlAttribute ("RoughAge")] public int Age;
}

XML element order:

public class Person
{
  [XmlElement (Order = 2)] public string Name;
  [XmlElement (Order = 1)] public int Age;
}

XmlSerializer and subclassing:

[XmlInclude (typeof (Student))]
[XmlInclude (typeof (Teacher))]
public class Person { public string Name; }

public class Student : Person { }

public class Teacher : Person { }
public void SerializePerson (Person p, string path)
{
  XmlSerializer xs = new XmlSerializer (typeof (Person));
  using (Stream s = File.Create (path))
    xs.Serialize (s, p);
}

Alternative:

XmlSerializer xs = new XmlSerializer (typeof (Person),
                     new Type[] { typeof (Student), typeof (Teacher) } );

Serializing child objects:

public class Person
{
  public string Name;
  public Address HomeAddress = new Address();
}

public class Address { public string Street, PostCode; }
Person p = new Person(); p.Name = "Stacey";
p.HomeAddress.Street = "Odo St";
p.HomeAddress.PostCode = "6020";

Subclassed child objects, solution 1:

[XmlInclude (typeof (AUAddress))]
[XmlInclude (typeof (USAddress))]
public class Address { public string Street, PostCode; }

public class USAddress : Address {  }

public class AUAddress : Address {  }

public class Person
{
  public string Name;
  public Address HomeAddress = new USAddress();
}

Subclassed child objects, solution 2:

public class Address { public string Street, PostCode; }

public class USAddress : Address {  }
public class AUAddress : Address {  }

public class Person
{
  public string Name;

  [XmlElement ("Address", typeof (Address))]
  [XmlElement ("AUAddress", typeof (AUAddress))]
  [XmlElement ("USAddress", typeof (USAddress))]
  public Address HomeAddress = new USAddress();
}

Serializing collections with the outer element:

public class Person
{
  public string Name;

  [XmlArray ("PreviousAddresses")]
  [XmlArrayItem ("Location")]
  public List<Address> Addresses = new List<Address>();
}

Serializing collections without the outer element:

public class Person
{
  public string Name;

  [XmlElement ("Address")]
  public List<Address> Addresses = new List<Address>();
}

Subclassing collection elements with the outer element:

[XmlArrayItem ("Address",   typeof (Address))]
[XmlArrayItem ("AUAddress", typeof (AUAddress))]
[XmlArrayItem ("USAddress", typeof (USAddress))]
public List<Address> Addresses = new List<Address>();

Subclassing collection elements without the outer element:

[XmlElement ("Address",   typeof (Address))]
[XmlElement ("AUAddress", typeof (AUAddress))]
[XmlElement ("USAddress", typeof (USAddress))]
public List<Address> Addresses = new List<Address>();

IXmlSerializable:

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

public class Address : IXmlSerializable
{
  public string Street, PostCode;

  public XmlSchema GetSchema() { return null; }

  public void ReadXml(XmlReader reader)
  {
    reader.ReadStartElement();
    Street   = reader.ReadElementContentAsString ("Street", "");
    PostCode = reader.ReadElementContentAsString ("PostCode", "");
    reader.ReadEndElement();
  }

  public void WriteXml (XmlWriter writer)
  {
    writer.WriteElementString ("Street", Street);
    writer.WriteElementString ("PostCode", PostCode);
  }
}

© 2007, O'Reilly Media, Inc. All rights reserved

C# 3.0 in a Nutshell
Buy from amazon.com Available now