Introduction to Serialization
http://www.csharpfriends.com
World's Greatest C# Community    
Home Articles C# Forums Books C# Syntax C# Spec C# Jobs free Source Code Advertise About
 

Control Panel

[ Sign In / register ]
Points   
Notes 
My Forums
My Tutorials
My Profile

Resources

Learn
 Articles
 QuickStarts
 C# Spec
 Whitepapers
 Tools
 Class Browser
 C# Code Generator
 Links
 Misc Rss Feeds
 Code Highlight
 411 Directory
 FREE magazines
 freevb.net

Reviews
  ASP.NET Hosting

Source Code
 Get Version 1.0



C# Consulting
AspDotNetStoreFront
Chapter:   UnCategorized
Current Lesson:
Introduction to Serialization
[Latest Content]
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | ALL
[prev. Lesson]  Nested Classes [next Lesson]  Com Interop Tutorial
Introduction to Serialization
  by: Rincewind

Introduction to Serialization

by: Rincewind

Introduction

Serialization is a process where an object is converted to a form able to be stored or transported to a different place. Such purpose can be if we want to save our object's data to a hard drive for example. Even more useful is its appliance when we want to send our object over some kind of network. Without serialization the remoting will be impossible.

Every modern language contains a feature to allow you to serialize your objects. The good news is that with C# and the .NET Framework you can do this smart and easily.

Making a class serializable

We have a class. What do we have to do to make it serializable?

First: Add the [Serializable] attribute in front of the class' definition. Second: I am joking… there is no second step. Yes that was all!

Take a look at the class below:
    [Serializable]
public class Store
    {
        private int sCount;        

        public int stockCount
        {
            get
            {
                return sCount;
            }
            set
            {
                sCount = value;
            }
        }

        [NonSerialized] private int temp;

        private string storeName = "My local store";

        public Store()
        {
            stockCount = 0;
        }

        // Here may follow some code for this class to function
    }
I have created this class to explain you the matter. It may contain much more fields, properties and methods and making it serializable will still be easy.

Here is a field 'sCount', a field 'temp' and a property 'stockCount'. The properties themselves are not serialized. Although they behave as variables they are only methods (a getter and a setter). So what is serialized is the field 'sCount' on which the 'stockCount' property depends.

Look at the 'temp' variable. As it name suggest it is probably used for some temporary operations and its value outside the context is of no use to us. In front of it I have put the [NonSerialized] attribute. It is there to specify that we do not want this field serialized.

Try to put the [NonSerialized] attribute in front of the 'stockCount' property. You get 'Attribute 'NonSerialized' is not valid on this declaration type. It is valid on 'field' declarations only.' error. Just to prove you that you can not serialize the properties but the fields they depend on.

Serialization

Now we have a class that can be serialized. What we need to do is to actually do something with it. We will store it to the hard drive.

Look at the code:
    Store myStore = new Store();
    myStore.stockCount = 50;

    FileStream flStream = new FileStream("MyStore.dat", 
    FileMode.OpenOrCreate, FileAccess.Write);
    try
    {
        BinaryFormatter binFormatter = new BinaryFormatter();
        binFormatter.Serialize(flStream, myStore);
    }
    finally
    {
        flStream.Close();
        tbOutput.Text += Environment.NewLine + "Object 'myStore' serialized!";
        tbOutput.Text += Environment.NewLine + "stockCount = 50";
    }
First we create an instance of our class. Then we change the value of 'stockCount' property to 50, it is 0 by default.

Now we are ready to store it. A FileStream class' instance is created. We are going to store the data to a file in binary format.

We will need the so called 'formatter'. In this case it is a binary one called 'BinaryFormatter'. It can be found in System.Runtime.Serialization.Formatters.Binary namespace.

For the actual storing of the file to the stream we use the 'Serialize' method of the class. And that's all. We just have to close the stream and we are ready.

The last two lines are to output something to a TextBox called 'tbOutput'. For the full source code look at the attached project.

Accessory notes:

This explanations are not related to the Serialization subject but are useful so you can read them if you want.

I have put Environment.NewLine constant to create new line. You can use '\r\n' but it can be different if run on a different platform like Linux or FreeBSD. The thing that is really surprising is the existence of this constant. We all know that Microsoft used to think that Windows is the only OS in the world.

My second note is about the exception handling. You must always use it to free the resources you have allocated. In this case we close our opened file. If we do not close it we may have problems like not being able to open it again (sharing violation).

And there is one more thing about the exception handling. Do not use the try + catch construction only to suppress errors. Leave the exceptions and if you process some of them but not all, throw the not processed to be processed at some higher level.

Deserialization

The code for deserializing an object is as simple as the one for its serialization. Take a look at it below:
    Store readStore = new Store();

    FileStream flStream = new FileStream("MyStore.dat",
        FileMode.Open, FileAccess.Read);
    try
    {
        BinaryFormatter binFormatter = new BinaryFormatter();
        readStore = (Store)binFormatter.Deserialize(flStream);
    }
    finally
    {
        flStream.Close();
        tbOutput.Text += Environment.NewLine + "Object 'readStore' deserialized!";
        tbOutput.Text += Environment.NewLine + "stockCount = " 
            + System.Convert.ToString(readStore.stockCount);
    }
We create an object. We create a an instance of FileStream to read the file. We create a BinaryFormatter object again.

This time we use its 'Deserialize' method. It returns the deserialized object which we cast to our own class 'Store'.

And in the end we close the stream and output the 'stockCount' value to check if everything is OK.

The SOAP Formatter

I have used the binary formatter till now. The fact that it is separated in a detached namespace may be already gave you a hint that there are other types of formatters.

The SOAP formatter serializes your object to an XML file obeying special rules. The SOAP is the standardized format which all the web services use.

You can replace the BinaryFormatter with the SoapFormatter and it will work with no other changes. The only thing you have to do is to add a reference to the System.Runtime.Serialization.Formatters.Soap (not only a uses clause but a reference too!).

The SOAP format being an XML is readable by humans unlike the binary one. So I wrote a function to show the actual code which the object is converted to.

Here is the code: Store myStore = new Store();
Store myStore = new Store();

MemoryStream memStream = new MemoryStream();
    try
    {
        myStore.stockCount = 50;
        SoapFormatter soapFormatter = new SoapFormatter();
        soapFormatter.Serialize(memStream, myStore);

        byte[] buff = memStream.GetBuffer();

        string soapOutput = "";

        foreach(byte b in buff)
            soapOutput += (char)b;

        tbOutput.Text += Environment.NewLine +
"Object 'myStore' serialized to SOAP!"; tbOutput.Text += soapOutput; } finally { memStream.Close(); }
Here I use a MemoryStream instead of the FileStream I used previously. This way we save ourselves the use of file.

After we store the data to the stream we get its contents with GetBuffer(). I didn't close the memStream right after the write as I did with the FileStream. This is because we loose the stored data once we close it.

Look at the code:
    foreach(byte b in buff)
        soapOutput += (char)b;
It is used to convert the byte array to string. Do you notice something wrong? If not, you have to study more about the strings. In C# the strings can not be changed once their value is set. This way we create a new string every time we add a character. More appropriate is the use of StringBuffer. In our case the buffer is small and the very serious speed losses are not visible.

Actually there is a function to convert a byte array to string but I do not remember what it was. It will be nice if someone mentions it :)

The output we get by the SOAP formatter is 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:Store id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/ 
SerializeTest/SerializeTest%2C%20Version%3D1.0.938.186%2C
%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
            <sCount>50</sCount>
            <storeName id="ref-3">My local store</storeName>
        </a1:Store>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here is a bit shortened version, I have replaced the long lines end with '...' so it is more easy to see its structure.
<SOAP-ENV:Envelope xmlns:xsi=...>
<SOAP-ENV:Body>
<a1:Store id="ref-1" xmlns:a1=...>
<sCount>50</sCount>
<storeName id="ref-3">My local store</storeName>
</a1:Store>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The interesting about this is the fact that this is the very same strings your web services communicate with.

Custom Serialization & Deserialization

We may need more control over the serialization process. We can achieve it by custom serialization. It allows us to make everything on our own.

To implement custom serialization we use the ISerializable interface. I have created a second class called 'CustomStore' a copy of the 'Store' class.

First make it so it implements the ISerializable interface:

public class CustomStore: ISerializable

The only method we have to implement because of this interface is 'GetObjectData':
public void GetObjectData(SerializationInfo serInfo, 
        StreamingContext streamContext)
    {
        serInfo.AddValue("stockCount", stockCount);
}
It is used when the serialization is performed. The line which it contains means that we want to store the value of the 'stockCount' property. To allow the deserialization of the object we have to define a special constructor with the same parameters as 'GetDataObject':
public CustomStore(SerializationInfo serInfo, 
        StreamingContext streamContext)
    {
        stockCount = serInfo.GetInt32("stockCount");
    }
The code in it is used to initialize the values of our variables according to the way we stored them.

Notice that here I use the property name not the field 'sCount' which looks a bit like we are serializing the property. We do not of course.

The same serialization and deserialization code we used for the standard serialization will work here too.

Differences

In the custom serialization we can use the [NonSerialized] attribute but it has no effect! Whether a variable is serialized depends solely on our code in the GetObjectData method.

To demonstrate this I have added another field called 'someVar'. If you change the previous code so it shows its value in the first class you will see that its value has been stored. If you try it in the second one - 'CustomStore' it will not be stored; we didn't write any code for that.

Let's make another experiment. Add the [NonSerialized] attribute to the 'sCount' field. Try it for the first class. The value returned after the deserialization is 0. So it works as expected. Put the [NonSerialized] in front of the 'sCount' field of the 'CustomStore' class. This should mean that the value should not be serialized. The returned value is 50.

Final notes

The deserialized objects are not created as usual. Their constructor is not called. So keep in mind that and pay attention to the fact that there may cause some subtle issues.



Download the code:

 Click Here

1 


Build Your Own ASP.NET Website Using C# & VB.NET

Chapter:  UnCategorized
Current Lesson:
Introduction to Serialization
[Latest Content]
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | ALL
[prev. Lesson]  Nested Classes [next Lesson]  Com Interop Tutorial



Today's Top Movers
vulpes 6800
MadHatter 2220
jal 867
Jeff1203 857
muster 791

Yesterday Top Movers
shakti sin.. 9
MadHatter 3
Al_Pennywo.. 2
C#fanatic 2
eluvan 1



Monthly Leaders
vulpes 6800
MadHatter 2260
jal 867
Jeff1203 857
muster 791

Top Members
mosessaur 18457
Rincewind 7074
stanleytan 6995
vulpes 6800
Gsuttie 6046

Great Offers
.net hosting
Go To My Pc
Remote Pc Control
zonealarm
spam blocker
web hosting directory
ad server   C#
snadtech GoToMyPc

Top of Page

Advertise | About | Link To Us | Privacy Notice Copyright © 2003 - 2005 CSharpFriends.com  All Rights Reserved  Visual C# Developer Center