DataContractSerializer Basics

By Peter Bromberg

Basics of using DataContractSerializer independent of WCF services

The DataContractSerializer class serializes and deserializes an instance of a type into an XML stream or document using a supplied data contract. You do not need to be using WCF to take advantage of this class.

Namespace: System.Runtime.Serialization
Assembly: System.Runtime.Serialization (in System.Runtime.Serialization.dll)

Many developers assume that the DataContractSerializer is only for use with WCF Services. This is not true. You can use the DataContractSerializer class to serialize and deserialize instances of a type into an XML stream or document for any purpose. The only requirement is to apply the DataContractAttribute attribute to classes, and the DataMemberAttribute attribute to class members to specify properties and fields that you want to be serialized.

To use the DataContractSerializer, you first create an instance of a class and an object appropriate to writing or reading the format -- for example, an instance of the XmlDictionaryWriter. Then you call the WriteObject method to persist the data. To retrieve data, you create an object appropriate to reading the data format (such as an XmlDictionaryReader for an XML document) and call the ReadObject method.

You can also set the type of a data contract serializer using the <dataContractSerializer> element in a client application configuration file.

Annotating Classes for Serialization or Deserialization

The DataContractSerializer is used in combination with the DataContractAttribute and DataMemberAttribute classes. To set up a class for serialization, apply the DataContractAttribute to the class. For each member of the class that returns data that you want to serialize, you must apply the DataMemberAttribute. You can serialize fields and properties, regardless of accessibility: private, protected, internal, protected internal, or public.

The final format of the XML does not need not be text. The DataContractSerializer writes the data as an XML infoset, which allows you to write the data to any format recognized by the XmlReader and XmlWriter. Microsoft recommends that you use the XmlDictionaryReader and XmlDictionaryWriter classes to read and write, because both are optimized to work with the DataContractSerializer.

NOTE: If you are creating a class that has fields or properties that must be populated before the serialization or deserialization occurs, you can use callback attributes, check out Version Tolerant Serialization Callbacks on MSDN.

Adding to the Collection of Known Types

When you serialize or deserialize an object, it is required that the type is "known" to the DataContractSerializer. Begin by creating an instance of a class that implements IEnumerable<(Of <(T>)>) (such as List<(Of <(T>)>)) and adding the known types to the collection. Then you create an instance of the DataContractSerializer using one of the overloads that takes the IEnumerable<(Of <(T>)>) (for example, DataContractSerializer(Type, IEnumerable<(Of <(Type>)>)).

Forward Compatibility

The DataContractSerializer also understands data contracts that have been designed to be compatible with future versions of the contract. Such types implement the IExtensibleDataObject interface. The interface features the ExtensionData property that returns an ExtensionDataObject object. For more information, you can review Forward Compatible Data Contracts on MSDN.

Here is a simple example of how to use the DataContractSerializer -- completely independent of Windows Communications Foundation. We'll have a simple Windows Forms app that uses a button and a multiline TextBox to display what we're doing. First, here is the class that applies the DataContract and DataMember attributes:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace DataContractSerializerDemo
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml;
// You must apply a DataContractAttribute or SerializableAttribute
// to a class to have it serialized by the DataContractSerializer.
[DataContract(Name = "Person", Namespace = "http://www.mypeople.com")]
class Person : IExtensibleDataObject
{
[DataMember()]
public string FirstName;
[DataMember]
public string LastName;
[DataMember()]
public int ID;
public Person(string newfName, string newLName, int newID)
{
FirstName = newfName;
LastName = newLName;
ID = newID;
}
private ExtensionDataObject extensionData_Value;
// this illustrates using the versioning ExtensionData property, which is not used in the example.
public ExtensionDataObject ExtensionData
{
get
{
return extensionData_Value;
}
set
{
extensionData_Value = value;
}
}
}
}

And here is the Form class that handles the demo:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace DataContractSerializerDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Test t = new Test( this);
t.Main();
}
}
public class Test
{
private static Form1 Form1;
private static MemoryStream writer = null;
public Test( Form1 form)
{
Form1 = form;
}
public void Main()
{
try
{
WriteObject();
ReadObject();
}
catch (SerializationException serExc)
{

Form1.textBox1.Text+="Serialization Failed";
Form1.textBox1.Text+=serExc.Message;
}
catch (Exception exc)
{
Form1.textBox1.Text+=
"The serialization operation failed: "+
exc.Message +exc.StackTrace;
}

}
public static void WriteObject()
{
List<Person> personList = new List<Person>();
Form1.textBox1.Text+=
"Creating a personList object and serializing it:\r\n";
Person p1 = new Person("Daddy", "Longlegs", 101);
Person p2 = new Person("Jack", "Spratt", 102);
Person p3 = new Person("Brad", "Pitt", 103);
personList.Add(p1);
personList.Add(p2);
personList.Add(p3);
writer = new MemoryStream();
DataContractSerializer ser =
new DataContractSerializer(typeof(List<Person>));

ser.WriteObject(writer, personList );
Form1.textBox1.Text += System.Text.Encoding.UTF8.GetString(writer.ToArray());
Form1.textBox1.Text += "\r\n";
writer.Seek(0, 0);// Always rewind to beginnning of stream!
}
public static void ReadObject()
{
// Note we re-use the MemoryStream as it has not been closed.
Form1.textBox1.Text += "===============================\r\n";
Form1.textBox1.Text+="Deserializing an instance of the object:\r\n";
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(writer, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(List<Person>));
// Deserialize the data and read it from the instance.
List<Person> deserializedPerson =
(List<Person>)ser.ReadObject(reader, true);
reader.Close();
Form1.textBox1.Text+=String.Format("{0} {1}, ID: {2}",
deserializedPerson[0].FirstName, deserializedPerson[0].LastName,
deserializedPerson[0].ID);
Form1.textBox1.Text += "\r\n" +String.Format("{0} {1}, ID: {2}",
deserializedPerson[1].FirstName, deserializedPerson[1].LastName,
deserializedPerson[1].ID);
Form1.textBox1.Text += "\r\n" + String.Format("{0} {1}, ID: {2}",
deserializedPerson[2].FirstName, deserializedPerson[2].LastName,
deserializedPerson[2].ID);
}
}
}

When you run this and click the button, you'll see the following display:

Using the DataContractSerializer, you have serialized a collection of type Person into a MemoryStream, and then deserialized it back into a collection of type Person and displayed the results.

There is another good reason to become familiar with DataContractSerializer and its brethren: Often, developers will start by "designing XML" as if it were a business object, and find themselves later attempting to "make it fit" into some sort of object. That's backwards. Create the POCO entity first, and ask the DataContractSerializer to create your XML from it. You'll be assured that it will all "round trip", and you'll have the advantage of Business Domain Entities that can be used to build a back-end persistence layer- whether with Entity Framework, or tools such as NHibernate.

Think of different ways you can use this technique. I bet you can come up with several right away. You can download the Visual Studio 2008 solution here.


Popularity  (13673 Views)
Picture
Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Follow Microsoft MVP
Create New Account
Article Discussion: DataContractSerializer Basics
Peter Bromberg posted at Monday, February 22, 2010 6:16 PM
Clement replied to Peter Bromberg at Tuesday, October 12, 2010 5:48 AM
Thank you for the very clear explanation.

I was wondering what are the benefits of using XmlDictionaryWriter over other writer. Is it just about space ? Is there a binaryWriter ?

One thing I haven't figured out how to do is how to serialize of IEnumerable type that has its own properties.
The only work around I know is to have a wrapper class which has an IEnumerable property and other necessary property but that's far from ideal.

Also It would be great if there was a post on how to handle versioning, adding properties, removing them, renaming them, etc...
Clement replied to Clement at Tuesday, October 12, 2010 5:48 AM
I should say also silverlight seems to be really lacking versioning support.
Peter Bromberg replied to Clement at Tuesday, October 12, 2010 5:48 AM
XmlDictionaryWriter is an abstract class. There is no binarywriter, to do that you should serialize using the BinaryFormatter class. The links to MSDN pages in the article above point to examples of how to do versioning as well as how to serialize IEnumerable types. IEnumerable is inmplemented by most of the classes in System.Collections as well  as IEnumerable<T> which is implemented via System.Collections.Generic.
Peter Bromberg replied to Clement at Tuesday, October 12, 2010 5:48 AM

Regarding versioning support, you may find the following blog post from the CLR team helpful:

http://blogs.msdn.com/clrteam/archive/2009/12/01/sharing-silverlight-assemblies-with-net-apps.aspx

XmlDictionaryWriter has a CreateBinaryWriter method that creates an instance of XmlDictionaryWriter that writes Silverlight binary XML format, if that's what you meant by "binary". Otherwise if you want true compact binary serialization, then you do not want to use XML at all as it is too verbose. Use the BinaryFormatter to convert a class instance to a compact byte array. In Silverlight, you can use Fast Binary Serialization or teh AltSerializer (found on Codeplex.com) to achieve even better results.

Clement replied to Peter Bromberg at Tuesday, October 12, 2010 5:48 AM
Thanks for the tips.
Perry replied to Peter Bromberg at Tuesday, October 12, 2010 5:48 AM
Thanks Peter for writting this. It is really useful.

I have one question about DataContractSerializer and XmlSerializer. When you annotate DataContract attribute with DataMember, the WCF will automatically use DataContractSerializer to serialize that attribute.

Could you tell me how could verify it? and how can I tell WCF to use XmlSerializer explicitly?

Thanks.