Serialization with .NET

Initializing and manipulating objects in a program is fairly straightforward, but in some situations, this isn't enough. Rather than just being used and discarded, it's sometimes necessary for an object to be preserved for later use. It's also sometimes necessary for objects to be sent to another computer. This process of preserving objects is known as serialization.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 7
August 14, 2007
Rate this Article:
MEH MEH++


SEARCH ASP FREE

See Also:

TOOLS YOU CAN USE

advertisement

When an object is serialized, its state is stored in such a way that the object can be easily remade at a later time or on a different machine. .NET provides several methods of serializing (and deserializing) objects, as well as several formats in which the object can be saved. This article will explore the serialization functionality provided by .NET.

Simple Serialization

To get started, let's serialize a simple object: a string containing a short message. We'll store the string on disk and then restore it by reading the file. Here's the required code, which we'll examine in detail:

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

 

class StringSerialization

{

   public static void Main(string[] args)

   {

       // The string we want to serialize

       string message = "This is a message.";

       Console.WriteLine(message);

       

       // Serialization takes place here

       FileStream myStream = File.Create("message");

       BinaryFormatter formatter = new BinaryFormatter();

       formatter.Serialize(myStream, message);

       

       // Clear our string

       message = null;

       

       // Restore the contents of our string -- deserialization

       myStream.Position = 0;

       message = (string)formatter.Deserialize(myStream);

       myStream.Close();

       Console.WriteLine(message);

   }

}

The above code saves the message in a file named message. It then clears the contents of the message (just for fun) and then restores the saved object.

As you can see, .NET's mechanisms for serialization are located in the System.Runtime.Serialization namespace. Here, we make use of the namespace System.Runtime.Serialization.Formatters.Binary. Formatters contains, as its name implies, different formatters for serialization. The formatter is in charge of exactly how the object to be serialized is represented. Here, we use BinaryFormatter. You can see how BinaryFormatter formats our string by examining the contents of the message file, which look something like this:

����This is a message.

Since we're only serializing a string, the file remains somewhat readable, with the exception of a number of special characters. However, with more complicated objects, the output produced by BinaryFormatter is quite unreadable. In many situations, it doesn't matter what the serialized object looks like; you may not care. However, in other situations, the serialized object may need to be more human-readable. This is a downside to the BinaryFormatter, but, thankfully, this isn't the only way to represent serialized data in .NET.

After message is initialized, a FileStream is created, which provides read/write access:

FileStream myStream = File.Create("message");

Note, however, that any type of Stream will do. Next, we create a BinaryFormatter, whose function was already explained:

BinaryFormatter formatter = new BinaryFormatter();

The object is then serialized simply by calling the Serialize method of our BinaryFormatter object:

formatter.Serialize(myStream, message);

Next, our position in myStream is reset to the beginning of the file so that we can begin reading. The process of deserialization is no less simple than the process of serialization. The Deserialize method is called, and myStream is passed:

message = (string)formatter.Deserialize(myStream);

Deserialize returns the object contained within the Stream, which must be cast to the proper type -- here, it is string. Finally, we clean up by closing myStream.

Not hard, is it?

Serializing Custom Types

In the previous example, we serialized a string object. However, you are, of course, not limited to serializing types provided by the framework. The real purpose of serialization is to serialize your own types. Thankfully, the process of serializing custom types doesn't have to be difficult or complex. .NET itself does most of the work for you. All you have to do is tell it what can be serialized and what can't. This is done through attributes.

Let's take a look at a type called Person:

public class Person

{

   private string name;

   private int age;

   

   public Person(string name, int age)

   {

       this.name = name;

       this.age = age;

   }

   

   public string Name

   {

       get

       {

        return name;

       }

   }

   public int Age

   {

       get

       {

        return age;

       }

   }

}

We can't simply serialize an instance of Person, or any other custom type. This is because not all types are fit for serialization. It simply wouldn't make sense for some types to be serialized. Instead, we must tell .NET that instances of our type can be serialized. This is done through the Serializable attribute:

[Serializable]

public class Person

{

   ...

}

Now we're free to serialize instances of our type. The process for doing this is the same as it was for serializing a string object earlier. We create a Stream object (a FileStream in our example) and a Formatter object (we'll use a BinaryFormatter again), and then we simply make a call to the Serialize method, passing the Stream and the object to be serialized. To deserialize, we call the Deserialize method. Here's everything in action:

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

 

class PersonSerialization

{

   public static void Main(string[] args)

   {

       // Create a Person

       Person bob = new Person("Bob", 35);

       Console.WriteLine("{0}, age {1}.", bob.Name, bob.Age);

       

       // Serialize our Person

       FileStream myStream = File.Create("bob");

       BinaryFormatter formatter = new BinaryFormatter();

       formatter.Serialize(myStream, bob);

       

       // Deserialize our Person

       myStream.Position = 0;

       bob = (Person)formatter.Deserialize(myStream);

       myStream.Close();

       Console.WriteLine("{0}, age {1}.", bob.Name, bob.Age);

   }

}

Bob, age 35.

Bob, age 35.

Note that if we were to try to serialize a Person object without marking Person as Serializable, we would get a SerializationException:

Unhandled Exception: System.Runtime.Serialization.SerializationException:Type Person is not marked as Serializable.

...

Serializing Only Some Internal Data

The Serializable attribute, however, marks the entire class as being serializable. In our Person example, all of the internal data in bob is serialized. While this is fine in some cases, it may be undesirable in others. It may not make sense to serialize some internal data, and some internal data may contain sensitive material. For example, consider this class:

using System;

 

[Serializable]

public class User

{

   private string name;

   private DateTime sessionStartTime;

   

   public User(string name)

   {

       this.name = name;

       sessionStartTime = DateTime.Now;

   }

   

   public string Name

   {

       get

       {

        return name;

       }

   }

   public DateTime SessionStartTime

   {

       get

       {

        return sessionStartTime;

       }

   }

}

The class represents a computer user and contains a private field of type DateTime which represents the time that the user logged in. When an instance of User is initialized, sessionStartTime is set to the current time. However, it doesn't make sense to store this field because when the user logs off (which we'll represent by saving the class), the time is no longer valid. In order to prevent .NET from serializing a piece of data, it must be marked with the NonSerialized attribute:

[Serializable]

public class User

{

   ...

   [NonSerialized]

   private DateTime sessionStartTime;

   ...

}

Now, when a User object is serialized, sessionStartTime won't be included. Let's try this out:

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

 

class UserSerialization

{

   public static void Main(string[] args)

   {

       // Create a User

       User charles = new User("Charles");

       Console.WriteLine("{0}, logged on since {1}.", charles.Name, charles.SessionStartTime);

       

       // Serialize the User

       FileStream myStream = File.Create("charles");

       BinaryFormatter formatter = new BinaryFormatter();

       formatter.Serialize(myStream, charles);

       

       // Deserialize the User

       myStream.Position = 0;

       charles = (User)formatter.Deserialize(myStream);

       myStream.Close();

       Console.WriteLine("{0}, logged on since {1}.", charles.Name, charles.SessionStartTime);

   }

}

If we run the above code, we can see that sessionStartTime isn't serialized, just as we specified. However, something happens as a side effect, as can be seen from the output:

Charles, logged on since 8/2/2007 8:42:03 PM.

Charles, logged on since 1/1/0001 12:00:00 AM.

Since sessionStartTime is not serialized, .NET is forced to find a new value for it upon deserialization. It initializes it to the default value, which is evident above. Of course, this is not what we want. Instead, we want it to contain the date and time when the object was deserialized. To remedy this, .NET offers several attributes that can be applied to methods, such as OnDeserializing. Methods marked with OnDeserializing will be called during the deserialization process. We can add such a method to Person that will give sessionStartTime the proper value. Here's how it's done:

[Serializable]

public class User

{

   ...

   [OnDeserializing]

   internal void OnDeserializing(StreamingContext context)

   {

       sessionStartTime = DateTime.Now;

   }

}

As you can see, the method has no return type, and it accepts one parameter, a StreamingContext structure. This structure contains information about the serialization process, but here, we can safely ignore it. Any method taking advantage of OnDeserializing must have no return type and must accept a StreamingContext, just like our method.

Now, Person behaves as we want it to:

Charles, logged on since 8/2/2007 8:51:30 PM.

Charles, logged on since 8/2/2007 8:51:30 PM.

Other Attributes

Besides the OnDeserializing attribute, .NET contains other similar attributes. When one of these attributes is applied to a method, then the method is called in the appropriate stage of serialization or deserialization. Here's a simple class that makes use of this family of attributes:

using System;

using System.Runtime.Serialization;

 

[Serializable]

public class AttributeTest

{

   [OnSerializing]

   internal void OnSerializing(StreamingContext context)

   {

       Console.WriteLine("Serializing.");

   }

   

   [OnSerialized]

   internal void OnSerialized(StreamingContext context)

   {

       Console.WriteLine("Serialized.");

   }

   

   [OnDeserializing]

   internal void OnDeserializing(StreamingContext context)

   {

       Console.WriteLine("Deserializing.");

   }

   

   [OnDeserialized]

   internal void OnDeserialized(StreamingContext context)

   {

       Console.WriteLine("Deserialized.");

   }

}

Notice how all the methods look like the OnDeserializing method in our previous example in their return type and parameter. The attribute names should make it pretty easy to tell when each method is called. Nonetheless, let's see the serialization and deserialization of an instance of our type. However, instead of using a FileStream and wasting a file, let's use a MemoryStream:

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

 

class AttributeDemo

{

   public static void Main(string[] args)

   {

       AttributeTest test = new AttributeTest();

       

       // Serialize it

       MemoryStream myStream = new MemoryStream();

       BinaryFormatter formatter = new BinaryFormatter();

       formatter.Serialize(myStream, test);

       

       // Deserialize it

       myStream.Position = 0;

       test = (AttributeTest)formatter.Deserialize(myStream);

       myStream.Close();

   }

}

 

Serializing.

Serialized.

Deserializing.

Deserialized.

blog comments powered by Disqus
.NET ARTICLES

- .Net 4.5 Brings Changes
- Understanding Events in VB.NET
- Objects, Properties, Events and Methods in V...
- Install Visual Web Developer Express 2010
- Microsoft Gadgeteer an Open Source Alternati...
- Best DotNetNuke Modules
- Facebook Image Viewer in Visual Basic
- Murach`s ADO.NET 4 Database Programming with...
- 5 Must Have Visual Studio 2010 Extensions
- Dynamic Web Applications with ASP.NET Mono u...
- PDFSharp: HTML to PDF in ASP.NET 3.5 using V...
- Using the PDFSharp Library in ASP.NET 3.5 wi...
- Sending Email in ASP.NET 3.5 using VB.NET wi...
- ASP.NET 3.5 Role Based Security and User Aut...
- Creating ASP.NET Login Web Pages and Basic C...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 2 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials