Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / Windows Forms
Article

Serializing with .NET 2.0 Generics

Rate me:
Please Sign up or sign in to vote.
4.60/5 (21 votes)
23 May 20063 min read 72.3K   47   10
A simple base class to add serialization support to your data classes.

Introduction

On a project I was working on, we were in the need for serialization en mass. A lot of classes had to be serializable. Following are the requirements, the stages towards a solution, and a proposed solution.

Design goals:

  • It must be simple for the application programmer.
  • The serialization output should be XML.
  • Future changes in the serialization code must not involve making changes to the client code. E.g., we may have to encrypt the serialized object representation.

Using the code

Since this was my first date with serialization, I naturally went Googling to get a feeling for who I'm dating. It quickly seemed that serialization wasn't that hard. Many people suggested using the following wherever you need to serialize:

VB
Dim serializedObj As String
Dim serializer As New XmlSerializer(Me.GetType)
Dim writer As New StringWriter
serializer.Serialize(writer, Me)
serializedObj = writer.ToString()

Not so bad. You decorate your class with the <SERIALIZABLE()> attribute, and maybe hide a few public properties with XmlIgnore, and there you go. It was immediately ruled out to have the client code contain this code in every class. So what about a utility method that will serialize/deserialize an object? There are a few challenges here. The utility function must be able to handle any type of object. Until .NET 2.0, the answer would probably have been something along these lines:

VB
Public Shared Function Serialize(obj as Object) As String

That looks OK. Let's have a look at the Deserialize method as well, and see if there are any surprises hiding there. In order to deserialize, we need another piece of information that we had when serializing. We need to know what type to deserialize to. The following signature should give us enough information to get the job done.

VB
Public Shared Function Deserialize(xml As String, _
                       obj As Object) As Object

The client code would then look something like:

VB
' serialize
Dim instance1 As New TestClass
Dim serializedInstance As String = _
    SharedLib.Serialize(instance1)

' deserialize
Dim instance2 As New TestClass
instance2 = CType(SharedLib.DeSerialize(serializedInstance, _
                  instance2), TestClass)

At this point, I was in doubt. Should I stop here and accept something that I didn't feel very good about, or should I spend some more time to try to find a better solution? I had two problems with the above code. The client code was not intuitive enough, and there was still too much code needed to get the job done. From an object oriented perspective, I would much more like to have the methods on the class itself. I decided to look for something else. I hear you shout Generics, so here it comes, but with a twist. We'll leave the concept of utility functions all together. In a normal object model, you have objects that contain state and do actions. Most of us don't write helperLib.Drive(myCar), but rather myCar.Drive(). How can we apply that to the serialization problem at hand?

Let's first look at how we want the calling code to look like, and then on how to implement our serialization to match that.

VB
' serialize
Dim instance1 As New TestClass
Dim serializedInstance As String = instance1.Serialize()

' deserialize
Dim instance2 As TestClass1
instance2 = TestClass1.Deserialize(serializedInstance)

That's what I want the calling code to look like. Just to make things even between VB.NET and C#, I'll do the remaining part in C#. We want to add a Serialize() and a Deserialize() method to any class that needs to be serialized. In order to minimize the coding effort of the classes that will use this functionality in, we will write a generic base class with the implementation of the two methods.

Here is the base class implementation:

C#
[Serializable()]
public abstract class ContractBase<T>
{

  /// Must have default constructor for xml serialization
  public ContractBase()
  {
  }

  /// Create an xml representation of this instance
  public string Serialize()
  {
     XmlSerializer serializer = new XmlSerializer(this.GetType());
     using (StringWriter stream = new StringWriter())
     {
        serializer.Serialize(stream, this);
        stream.Flush();
        return stream.ToString();
     }
  }

  /// Creata a new instance from an xml string.
  /// The client is responsible for deserialization of the correct type
  public static T Deserialize(string xml) 
  {
     if (string.IsNullOrEmpty(xml))
     {
        throw new ArgumentNullException("xml");
     }

     XmlSerializer serializer = new XmlSerializer(typeof(T));
     using (StringReader stream = new StringReader(xml))
     {
        try
        {
           return (T)serializer.Deserialize(stream);
        }
        catch (Exception ex)
        {
           // The serialization error messages are cryptic at best.
           // Give a hint at what happened
           throw new InvalidOperationException("Failed to " + 
                            "create object from xml string", ex);
        }
     }
  }
}

Here is a sample class, using the serialization base class we just wrote:

C#
[Serializable()]
public class TestClass : ContractBase< TestClass >
{
  private string m_firstName;
  public string FirstName
  {
     get
     {
        return m_firstName;
     }
     set
     {
        if (m_firstName == value)
           return;
        m_firstName = value;
     }
  }

Note that the TestClass inherits from a generic base class, passing in its own type as the generic type.

The calling code now looks like what we wanted.

C#
//serialize
TestClass instance1 = new TestClass();
string serializedInstance = instance1.Serialize();

//deserialize
TestClass instance2;
instance2 = TestClass.Deserialize(serializedInstance);

See a better solution? Please let me know.

You can also visit my blog.

History

  • First revision.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Generalstyle Pin
bryce22-Apr-09 15:45
bryce22-Apr-09 15:45 
Generalthanks a lot Pin
almanzora200026-Jan-07 17:05
almanzora200026-Jan-07 17:05 
GeneralXmlSerializer short-comings Pin
Orium112-Oct-06 9:52
Orium112-Oct-06 9:52 
GeneralRe: XmlSerializer short-comings Pin
Kim Major2-Oct-06 10:48
Kim Major2-Oct-06 10:48 
Generalvb conversion Pin
constantnet21-Sep-06 8:41
constantnet21-Sep-06 8:41 
GeneralRe: vb conversion [modified] Pin
Kim Major21-Sep-06 21:50
Kim Major21-Sep-06 21:50 
GeneralWell writen Pin
dmeccl8-Jun-06 22:20
dmeccl8-Jun-06 22:20 
GeneralNicely done Pin
C-codist27-May-06 7:57
C-codist27-May-06 7:57 
GeneralRe: Nicely done Pin
Kim Major27-May-06 22:26
Kim Major27-May-06 22:26 
GeneralSerializer and Deserializer to file Pin
THMok23-May-06 16:49
THMok23-May-06 16:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.