Serialization in the .NET Framework

By Robert Chartier


Serialization in .NET allows the programmer to take an instance of an object and convert it into a format that is easily transmittable over the network, or even stored in a database or file system. This object will actually be an instance of a custom type, including any properties or fields you may have set.


A few examples I can think of include the ability to send an instance of your object to another portion of a local or remote application, such as over a Web service. Another example of when to choose to serialize objects is for data within objects that would normally be stored in a database, but these pieces of data do not need to be stored individually (their own fields in your tables). Streamline the database design by only holding those pieces of data for each record you need to query on, and the rest of the pieces can simply be serialized within a custom type and stored as a complete object within the database.


Let’s take a scheduling object as an example. The scheduling object, enables an application to trigger events based on a very specific schedule. There could be numerous settings involved when building this, such as intervals, date and time restrictions, etc. Normally each piece of data would require the creation of a separate field in your table, when really the only important piece of information is the exact date/time when the next event should be triggered. Instead, serialize this entire scheduling object, and save that into the database, along with the one piece of information that is needed, the next event date/time. This cuts down the entire database schema to about three fields, not to mention the time saved from writing complex Save and Load methods.


This article covers the two formats in which you can serialize objects to –: XML or binary — and the many advantages/disadvantages of each. It also covers the two ways that one can serialize, Basic and Custom. And, finally, the article will end with a sample project demonstrating how to send a custom object over the wire with Web Services.


This piece focuses on the .NET Framework’s System.Runtime.Serialization namespace (see http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeSerialization.asp).

The first step in any serialization process is to take the instance of the object and convert it to a memory stream. From there we have the ability to perform any number of operations with (file IO, database IO, etc.). Examine Figure 1.1 (below) which demonstrates four methods for performing serialization, including serializing and de-serializing in binary format, and the same in XML format. The next section will cover the differences between binary and XML serialization.


Figure 1.1 Core Serialization Methods



#region Binary Serializers
public static System.IO.MemoryStream SerializeBinary(object request) {
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, request);
return memStream;
}

public static object DeSerializeBinary(System.IO.MemoryStream memStream) {
memStream.Position=0;
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
object newobj = deserializer.Deserialize(memStream);
memStream.Close();
return newobj;
}
#endregion

#region XML Serializers

public static System.IO.MemoryStream SerializeSOAP(object request) {
System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer =
new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, request);
return memStream;
}

public static object DeSerializeSOAP(System.IO.MemoryStream memStream) {
object sr;
System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer =
new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
memStream.Position=0;
sr = deserializer.Deserialize(memStream);
memStream.Close();
return sr;
}
#endregion

Whenever you want an object to be able to be serialized, mark it as [Serializable()], with a custom attribute on the class (http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemserializableattributeclasstopic.asp). In the same solution provided here, the “Schedule” class in the “Serialization” project, you will see this attribute in use. Also consider the field attribute named “[NonSerialized()]” (see http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemnonserializedattributeclasstopic.asp). It can be applied to any field from within your class to prevent it from being serialized. This would be useful if you had a variable within the class that was not needed or you did not want to have serialized into the final product. Simply add the NonSerialized() attribute to that variable because the system will not automatically serialize it for you.


Keep in mind that the [Serializable()] attribute applies to the class entirely. So if you want the entire class to be able to support serialization, you must add that attribute, where the [NonSerialized()] attribute applies to the fields within a class that is marked as [Serializable()]. Note that using the [NonSerialized()] is sometimes called “Selective Serialization.”


Lastly, note that in order to use the System.Runtime.Serialization.Formatters.Soap.SoapFormatter, you must add a reference to the System.Runtime.Serialization.Formatters.Soap.dll.


Before proceeding, take time to look over our Schedule class listed in Figure 1.2 (below).


Figure 1.2 Schedule Class



using System;

namespace FifteenSeconds {
/// <summary>
/// Allow us to represent a schedule
/// </summary>
[Serializable()]
public class Schedule {
protected System.DateTime start;
protected System.DateTime end;

//number of milliseconds to increment @ each interval
protected long interval;

public System.DateTime Start {get{return start;}set{start=value;}}
public System.DateTime End {get{return end;}set{end=value;}}
public long Interval {get{return interval;}set{interval=value;}}
public Schedule(System.DateTime Start, System.DateTime End, long Interval) {
start=Start;
end=End;
interval=Interval;
}
//return the next runtime, and if the schedule has ended, return the end time
public System.DateTime NextRunTime {
get {
System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
if(ts.Milliseconds>0) {
//our end time is in the future still
return System.DateTime.Now.AddMilliseconds(interval);
} else {
return end;
}
}
}
}

}



This is the class referred to in the remainder of this article.


  • Download code for this article below.


    Binary vs. XML Serialization



    As demonstrated above, the choice between XML and Binary serialization is not dependent on the actual implementation of the code. Both methods are very easy to implement. Consider the advantages offered by both.


    Advantages of Binary Serialization



    1. All members, no matter if they are read-only will be serialized.
    2. Greater performance*


    Advantages of XML Serialization



    1. Greater flexibility of object sharing and usage (interoperability)
    2. No strict binary dependence
    3. Human readable



    After reviewing the code in Figure 1.1, notice that serialization in either binary or XML simply depends only on choosing your Formatter. Also notice that the difference between serialization and deserialization is the difference between calling the Serialize() method or the Deserialize() method with the appropriate parameters. The above code can easily be copied into any of your applications as is; it will be the core of most of your serialization needs. Consider taking a look at the attached ZIP file for this article. I have included those methods listed above, along with a few other methods.


    *I have included a spreadsheet that shows my test results for groups of iterations of Basic Serialization. Also take a look at the “SpeedTests” project in the solution, which you can use to run your own tests. For one simple iteration to serialize it was more than 1,000 percent faster to use binary serialization over XML, and for deserialization it was only 380 percent faster.



    Basic Serialization vs. Custom



    The .NET Framework provided the ability to perform serialization in two ways. Keep in mind that “performing” serialization is not the same as the “format” in which we serialize. Performing serialization refers to the actual way in which to instruct Framework to take our object and pack it into the final result of the serialization, regardless of the format (see section on Binary vs. XML Serialization, above). The easiest way is to allow Framework to automatically serialize the object on its own; this is typically called “Basic Serialization.” Basic Serialization only requires that your object has the [Serializable()] class attribute. Framework will take your class and convert it to the given formatter used. The only control you have over this process is by using the [NonSerialized()] field attributes to stop any field from being serialized. You do not have any control over exactly how each item in your class is serialized.


    If you desire greater control, then move into the world of “Custom Serialization.” This is where you can specify exactly which items will be serialized, and exactly how it will be done. In the sample solution provided, I have created a class named “ScheduleCustom” to demonstrate exactly how this is done.


    When using Basic Serialization, versioning your objects plays a major role. To serialize our schedule object to disk, and then change that object (for example, by adding an additional field), will lead to problems with deserialization. The error will be similar to:


    “Wrong number of Members. Object FifteenSeconds.Schedule has 4 members, number of members deserialized is 3.”


    If you do run into this issue, consider changing to Custom Serialization. Since you are exactly specifying how to handle all members of your class during the serialization process, you can avoid these versioning problems.



    Custom Serialization



    The first thing we must do is obviously mark our class as [Serializable()]. Next, our class must implement the System.Runtime.Serialization.ISerializable interface (http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeSerializationISerializableClassTopic.asp). So our class definition looks like:



    [Serializable()]
    public class ScheduleCustom : System.Runtime.Serialization.ISerializable {


    Within this interface we see the following single method:


    void GetObjectData(SerializationInfo info, StreamingContext context);


    In order for our class to satisfy supporting the interface, we must also implement this GetObjectData() method:


    public void GetObjectData(SerializationInfo info,StreamingContext context) {
    //use the info object to add the items you want serialized
    info.AddValue(“start”, start);
    info.AddValue(“end”, end);
    info.AddValue(“interval”, interval);
    }

    Given this extremely simple demonstration you may not be able to see the actual power of using Custom Serialization, so I will add to the class to demonstrate the flexibility it adds to your serialization needs.


    If we were to examine the output of the “start” DateTime value during the serialization process, we would see it default to the Framework DateTime format:


    2002-08-19T14:09:13.3457440-07:00


    If you plan on sharing this object with others who are not using the .NET Framework and are internationally distributed, it may be wise to represent the DateTime value in Greenwich Mean Time (GMT). Let’s modify the GetObjectData() method to convert “start” and “end” times into GMT.



    public void GetObjectData(SerializationInfo info,StreamingContext context) {
    //use the info object to add the items you want serialized
    //in order for better partnering we will convert our times to GMT times
    //”Universal Time” AKA “Coordinated Universal Time” AKA GMT
    info.AddValue(“start”, System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
    info.AddValue(“end”, System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
    info.AddValue(“interval”, interval);
    info.AddValue(“timeformat”, “utc”);
    }


    Now the “start” time, when serialized, will be in the GMT format of:


    8/19/2002 9:09:13 PM


    Also notice the additional property I added named “timeformat”. This item will be used later for us to determine which type of DateTime formats were used for the serialization process.


    Also consider other formats for outputting the dates. Consider what the System.Globalization.DateTimeFormatInfo could offer, or even converting the DateTime to time since Unix epoch (January 1, 1970, see http://www.wikipedia.com/wiki/Unix_epoch) for easier integration with non-Microsoft platforms. You could add an optional property to our class to indicate which type of format the DateTime will be in once it has been serialized, of course indicate this by using the “timeformat” value we added to the items being serialized.



    Custom Deserialization



    If you want to allow your object to be deserialized in a custom manner, use a custom constructor. The definition of this constructor looks similar to:



    public ScheduleCustom (SerializationInfo info,StreamingContext context) {}

    As usual, you must implement this method. For our schedule class, our implementation will convert the UTC time back into a local DateTime format.



    public ScheduleCustom (SerializationInfo info,StreamingContext context) {
    //let’s bring back our date/times into local times
    this.start = info.GetDateTime(“start”).ToLocalTime();
    this.end = info.GetDateTime(“end”).ToLocalTime();
    this.interval = info.GetInt32(“interval”);
    }

    As I indicated in the Custom Serialization process previously, the additional item “timeformat” could be used to specify exactly how you would convert the serialized time into the DateTime needed for the local object. But since this example simply covers the conversion to and from UTC, our task is very easy.



    Serialization with Web Services



    When learning a new aspect of technology, it’s useful to see that technology in use. In this section we will create one Web Service with two methods. The first method will enable Framework to return our custom serialized schedule object in binary format. The second method will return the same object in XML format.


    This example will be a quick one because we will build on top of the foundation we already built. In my solution, I have simply added a new C# Web Service project, with a reference to our “serialization” project. We will next add the two methods as described above.


    FifteenSeconds.ScheduleCustom customSchedule =
    new FifteenSeconds.ScheduleCustom(System.DateTime.Now, System.DateTime.Now.AddHours(5), 10000);

    [WebMethod]
    public byte[] Get_Binary_Schedule() {
    return FifteenSeconds.Serializer.Serialize( customSchedule,
    FifteenSeconds.Serializer.SerializationFormat.Binary).ToArray();
    }
    [WebMethod]
    public string Get_XML_Schedule() {
    return FifteenSeconds.Serializer.ConvertStreamToString
    (FifteenSeconds.Serializer.Serialize( customSchedule,
    FifteenSeconds.Serializer.SerializationFormat.Xml));
    }

    Notice both methods are using the same instance of the FifteenSeconds.ScheduleCustom object and simply return back either the byte[], in the case of the binary transfer, or the string, in the case of the XML.


    Don’t forget Framework will automatically serialize your objects for you, simply by having the return type on your method your custom type. But what happens if you need to send a collection of somewhat unrelated objects over the wire? It really depends on your needs and your imagination.


    Take time now to review the project named “Serialization” in the solution. It also has a few methods that you may find useful if you ever need to serialize these objects to a file on your disk.


    Conclusion



    This article displayed just how easy it is to use serialization in your applications. Whenever you do need to convert an instance of an object into a format that can easily be transmitted or saved, serialization may be an option.



    About the Author



    Robert Chartier has developed IT solutions for more than nine years with a diverse background in both software and hardware development. He is internationally recognized as an innovative thinker and leading IT architect with frequent speaking engagements showcasing his expertise. He’s been an integral part of many open forums on cutting-edge technology, including the .NET Framework and Web Services. His current position as vice president of technology for Santra Technology (http://www.santra.com) has allowed him to focus on innovation within the Web Services market space.

    He uses expertise with many Microsoft technologies, including .NET, and a strong background in Oracle, BEA Systems, Inc.’s BEA WebLogic, IBM, Java 2 Platform Enterprise Edition (J2EE), and similar technologies to support his award-winning writing. He frequently publishes to many of the leading developer and industry support Web sites and publications. He has a bachelor’s degree in Computer Information Systems.


    Robert Chartier can be reached at rob@aspfree.com.

  • More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read