.NET Serialization (using BinaryFormater, SoapFormatter and XmlSerializer)

Posted in Performance | Serialization at Sunday, 22 March 2009 13:06 GMT Standard Time

Object serialization is the process of converting an object to a format that is suitable for persistence (database, file, etc) or transportation (remoting, Web Services, MSMQ, etc).

The format of the output byte stream is governed by a formatter object. When you serialize data, you construct a formatter object that implements the required format.

With .NET Framework 2.0 and above you can use two formatters: BinaryFormatter and SoapFormatter. You can also serialize objects (well, just the public members, not the private ones - which is know as shallow serialization) by using the XMLSerializer class.

The following class Person implements some attributes that are used in the process of seralization. This class is also used on the application example you can download below.

 [Serializable] //<-- This atribute is just required by BinaryFormatter and SoapFormatter
    public class Person  //<-- XMLSerializer needs that the class is defined as public
    {
        //[System.Xml.Serialization.XmlIgnore]   //<-- using this attribute, this field will be ignored in Xml Serialization        

        public string FirstName; //Some few public properties to serialize. They  will be serialize by the three formatters.

        public string LastName;                
               
        [NonSerialized] //<-- Using this attribute, the field Nationality won't be serialized (by any of the three serializers)
        public string Nationality;        
        
        private string _Address; //this private field won't be serialized using XmlSerialization. They will be serialized using Binary or Soap formatters

        private string _ZIPCode; //this private field won't be serialized  using XmlSerialization. They will be serialized using Binary or Soap formatters

		//let's create a method to set the private properties.
        public void SetAddress(string address, string zipCode) 
        {
            _Address = address;
            _ZIPCode = zipCode;
        }

        public override string ToString()
        {
            return string.Format("I'm {0} {1} from {2}!. Address: {3}, {4}", 
				FirstName,LastName,Nationality, _Address,_ZIPCode);
        }
    }

Serialization formatters in the .NET Framework

1) BinaryFormatter (System.Runtime.Serialization.Formatters.Binary namespace)

  • Serializes and object in an internal binary format that the .NET Framework understands.
  • The type needs to be marked with the [Serializable] atribute (see class Person)
  • BinaryFormatter saves metadata (assembly and type information) on the output stream along with object data. This information is necessary to deserialize the data and rebuild the object in memory.

PROS

  • The output byte stream generated is compact
  • The serialization process is faster than using the other formatters.
  • This formatter can serialize generic and non generic collections (being the items within the collection serializable)
  • Serializes public and private members (deep serialization)

CONS

  • Format not readable by other techonolgies (just .NET Framework)

BinaryFormatter serialization use example

Person p=new Person();
string path=@"c:\myfile.bin";
using (System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
{
	System.Runtime.Serialization.Formatters.Binary.BinaryFormatter b = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
	b.Serialize(fs, p);
	fs.Close();
}

This code will serialize object p (type Person) on file c:\myfile.bin with binary format

BinaryFormatter deserialization use example:

string path=@"c:\myfile.bin";
using (System.IO.FileStream ds = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
	System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
	Person p = bf.Deserialize(ds) as Person;	
}

This code will deserialize object p (type Person) from c:\myfile.bin

2) SoapFormatter (System.Runtime.Serialization.Formatters.Soap namespace)

  • Serializes an object in SOAP format.
  • The type also needs to be marked with the [Serializable] atribute.
  • To use this class you need a reference to the System.Runtime.Serialization.Soap assembly.
  • This formatter also saves metadata (assembly and type information) on the output stream along with object data.

PROS

  • Follows a standard (SOAP) that other platforms can understand.
  • Serializes public and private members (deep serialization)

CONS

  • It is more verbose (less efficient) than BinaryFormatter.
  • It can NOT serialize generic collections (System.Collections.Generic namespace)

SoapFormatter serialization use example:

Person p=new Person();
string path=@"c:\myfile.soap";
using (System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
{
	System.Runtime.Serialization.Formatters.Soap.SoapFormatter
	f = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
	f.Serialize(fs, p);
	fs.Close();
}

This code will serialize object p (type Person) on file c:\myfile.soap with Soap format

SoapFormatter deserialization use example:

string path=@"c:\myfile.soap";
using (System.IO.FileStream ds = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
	System.Runtime.Serialization.Formatters.Soap.SoapFormatter sf = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
	Person p = sf.Deserialize(ds) as Person;
	AppendToLog(string.Format("Single person deserialized from {0} in SOAP format: {1}", path, p));
}

This code will deserialize object p (type Person) from file on c:\myfile.soap using Soap formatter

A Person object after Soap serialization looks like this:

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<SOAP-ENV:Body>
		<a1:Person id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/Serialization/dotneat_net.Serialization%2C%20Version%3D0.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
			<FirstName id="ref-3">Joe</FirstName>
			<LastName id="ref-4">Doe</LastName>
			<_Address id="ref-5">dotneat.net Street, Zaragoza, Spain</_Address>
			<_ZIPCode id="ref-6">50007</_ZIPCode>
		</a1:Person>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

3) XMLSerializer

  • Serializes and object in XML format.
  • Requeriments:
    • Type must be public (public class person)
    • Must implement a parameterless constructor (in order to deserialize the object)
    • If you are serializing a non generic collection of items, you must pass the types that are stored in the collection as a parameter in the constructor of the XmlSerializer (see example code).

PROS

  • It can serialize generic and non generic collections (being the items within the collection serializable)
  • Class doesn't need to be decorated with [Serializable] attribute.
  • Developer has a deep control about how each field is going to be serialized by using the attributes:
    • [XmlAttribute] : over a field, marks that the field will be serialized as attribute, instead of a node
    • [XmlIgnore] : won't serialize that field. The same as NonSerializable, but just for the XmlSerializer.
    • [XmlElement (ElementName="NewName"]: Allows you to rename the field when being serialized.
    • ....

CONS

  • Only public members will be serialize! (shallow serialization) (both BinaryFormatter and SoapFormatter would serialize also object private members)

XmlSerializer serialization use example:

Person p=new Person();
string path=@"c:\myfile.xml";
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Person));
using (System.IO.FileStream xmlStream = new System.IO.FileStream(path, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
	serializer.Serialize(xmlStream, p);
	xmlStream.Close();
}

This code will serialize object p (type Person) on file on c:\myfile.xml using the XmlSerializer class

XmlSerializer deserialization use example:

string path=@"c:\myfile.xml";
System.Xml.Serialization.XmlSerializer dxml = new System.Xml.Serialization.XmlSerializer(typeof(Person));
using (System.IO.FileStream xmlStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
	Person p = dxml.Deserialize(xmlStream) as Person;
	xmlStream.Close();
}                

This code will deserialize object p (type Person) from file c:\myfile.xml using the XmlSerializer class

A Person object after XML serialization looks like this (see how just public members are serialized):

<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FirstName>Joe</FirstName>
  <LastName>Doe</LastName>
</Person>

As you may see, private members are not serialized.

Selecting Fields to serialize

You can omit fields from being serialized by marking them with the [NonSerialized] attribute (see Nationality field in Person class).

Customizing the process of serialization and deserialization

If you are required to, you can modify the way the data in objects is serialized and deserialized. For example, you may want to serialize the account number and pass code of the class BackAccount crypted (just those two fields)

Serialization process

You need to implement the interface ISerializable on the class to serialize. If the type implements ISerializable interface, the formatter calls the GetObjectData to convert the object into the stream of bytes.

Use OnSerializingAttribute and OnSerializedAttribute to mark methods that will be executed before and after the serialization takes place.

[Serializable]
public class Person : ISerializable{

    .....

    [OnSerializing]
    public void Deserializing (StreamingContext context){
        //before serialization
    }


    [OnSerialized]
    public void Serialized (StreamingContext context){
        //after serialization
    }
}

Deserialization process

It is also possible to control how the deserialization process by implementing a constructor that takes a SerializationInfo and a StreamingContext as parameters.

[Serializable]
public class Person : ISerializable{
    //Deserialization constructor
    private Person(SerializationInfo info, StreamingContext context){
        string fname=info.GetString("FirstName");    
        // ...   
    }
}

Use OnDeserializingAttribute and OnDeserializedAttribute to specify methods to run before and after the object is deserialized.

[Serializable]
public class Person : ISerializable{

    .....

    [OnDeserializing]
    public void Deserializing (StreamingContext context){
        //before deserialization
    }


    [OnDeserialized]
    public void Deserialized (StreamingContext context){
        //after deserialization
    }

}

Example application code


(click on the image to view screenshot in full size)

Serialization application example source code (hosted on google code)

About the example application

You can use the application for serialize/deserialize a simple example class "Person". You can go and check file size and format of output file. You can also serialize/deserilize non generic (ArrayList) and generic (List) collections of Person, so you can see how the formatters serialize those types.

Performance

There is a performance test functionality. You can check which is the faster way of serializing a simple type. Feel free to extend the example to create performance tests that suits your needs

Final review

AddThis Social Bookmark Button

Backgroundworker example

Posted in .NET 2.0 | Performance | Visual Studio 2005 at Tuesday, 10 February 2009 17:57 GMT Standard Time

The background worker allows you to execute intense or long operations on a separate thread, without having to deal with threads, invokes or delegates. This is essential in today's cluttered webspace brought forth by the constant growth of broadband technology.

This simple example pretty much cover all posibilities of use of this component: cancellation support, backgroundworker error handling and report progress (also passing UserState data on report progress notifications)

Nota: Este artículo está disponible en castellano aquí

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BackgroundWorker
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //mandatory. Otherwise will throw an exception when calling ReportProgress method
            backgroundWorker1.WorkerReportsProgress = true; 

            //mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation
            backgroundWorker1.WorkerSupportsCancellation = true;
        }



        //This method is executed in a separate thread created by the background worker.
        //so don't try to access any UI controls here!! (unless you use a delegate to do it)
        //this attribute will prevent the debugger to stop here if any exception is raised.
        //[System.Diagnostics.DebuggerNonUserCodeAttribute()]
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            //NOTE: we shouldn't use a try catch block here (unless you rethrow the exception)
            //the backgroundworker will be able to detect any exception on this code.
            //if any exception is produced, it will be available to you on 
            //the RunWorkerCompletedEventArgs object, method backgroundWorker1_RunWorkerCompleted
            //try
            //{
                DateTime start = DateTime.Now;
                e.Result = "";
                for (int i = 0; i < 100; i++)
                {
                    System.Threading.Thread.Sleep(50); //do some intense task here.
                    backgroundWorker1.ReportProgress(i, DateTime.Now); //notify progress to main thread. We also pass time information in UserState to cover this property in the example.
                    //Error handling: uncomment this code if you want to test how an exception is handled by the background worker.
                    //also uncomment the mentioned attribute above to it doesn't stop in the debugger.
                    //if (i == 34)
                    //    throw new Exception("something wrong here!!");

                    //if cancellation is pending, cancel work.
                    if (backgroundWorker1.CancellationPending)
                    {
                        e.Cancel = true; 
                        return;
                    }
                }

                TimeSpan duration = DateTime.Now - start;
                
                //we could return some useful information here, like calculation output, number of items affected, etc.. to the main thread.
                e.Result = "Duration: " + duration.TotalMilliseconds.ToString() + " ms.";
            //}
            //catch(Exception ex){
            //    MessageBox.Show("Don't use try catch here, let the backgroundworker handle it for you!");
            //}
        }


        
        //This event is raised on the main thread.
        //It is safe to access UI controls here.
        private void backgroundWorker1_ProgressChanged(object sender, 
            ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage; //update progress bar
            
            DateTime time = Convert.ToDateTime(e.UserState); //get additional information about progress
            
            //in this example, we log that optional additional info to textbox
            txtOutput.AppendText(time.ToLongTimeString());
            txtOutput.AppendText(Environment.NewLine);            
        }



        //This is executed after the task is complete whatever the task has completed: a) sucessfully, b) with error c)has been cancelled
        private void backgroundWorker1_RunWorkerCompleted(object sender, 
            RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled) {
                MessageBox.Show("The task has been cancelled");
            }
            else if (e.Error != null)
            {                
                MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());
            }
            else {
                MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());
            }
            
        }




        private void btoCancel_Click(object sender, EventArgs e)
        {
            //notify background worker we want to cancel the operation.
            //this code doesn't actually cancel or kill the thread that is executing the job.
            backgroundWorker1.CancelAsync();
        }

        private void btoStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

      
    }
}

Download sample project

Download BackgroundWorker.ZIP (C# sample project)

AddThis Social Bookmark Button

Using a RAM drive to compile Visual Studio .NET projects

Posted in .NET 2.0 | Performance | Productivity | Visual Studio 2005 | RAM disk at Saturday, 15 December 2007 10:18 GMT Standard Time
(updated 22 dec 07)

After reading some blog posts like this I decide to test on my computers how important is hard disk speed in order to increase your productivity when compiling Visual Studio solutions with more than a few project.

I also wanted to see the gain if I placed my solution (more than 25 projects) in a RAM drive disk instead of the hard disk (be sure you backup frecuently if you do that, or get one of this things to avoid data lost).

In order to create a RAM drive, I download the trial version of Virtual Hard Drive Pro
 from FarStone.

There are the results:

1. Environment hardware information

My desktop:
  1. CPU: AMD Sempron, 1800 MHz
  2. RAM: 2 x (1 Gb Kingston)
  3. Hard disks (2):
    1. System: ATA-100 40 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 2Mb. Average read speed: 40 Mb/second (*).
    2. Data and code: SATA 200 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 8 Mb. Average read speed: 50 Mb/second (*).
  4. RAM drive speed (read): 900 Mb/sec
My laptop (Toshiba Satellite Pro M70):
  1. CPU: Intel Pentium Celeron 1.73 Ghz
  2. RAM: 2 x (512 Mb Toshiba)
  3. Hard disk (1) : Toshiba 5400 rpm. Average read speed: 30 Mb/second (*)
  4. RAM drive speed (read): 1500 Mb/sec
* (Hard disk speed tests have been done with HD_Speed from www.steelbytes.com)

2. The Microsoft Visual Studio solution

A Microsoft Visual Studio .NET 2005 solution with 26 projects (Windows Forms and Windows library projects mainly), in C#.

3. Results

Compilation:

Desktop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 72 seconds*
b) Compilation on a 500 MB RAM Drive : 71 seconds*

Laptop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 65 seconds*
b) Compilation on a 400 MB RAM Drive : 32 seconds*

*(average of three compilations)

4. Conclusions:

Seems like my desktop doesn't have much processor power, and the bottleneck is mainly on the CPU, since there is not much different results on readig from a 50 Mb/seconds hard disk or from a 900 MB/second one (the RAM Drive)

My laptop, instead has a slower disk drive but more processor power, so there is a lot of difference between compiling from hard disk and RAM drive. (it took half the time on the RAM disk - also RAM disk read speed is higher than in the desktop)

I still will do more tests with some other combinations of CPU and hard disk..

Feel free to do the above tests yourself and share the results.

Updated:
  • 22 dic 07: Added test with laptop. Desktop tests done again.

AddThis Social Bookmark Button