C# Tutorial - Serialize Objects to a File

Skill

C# Tutorial - Serialize Objects to a File

Posted in:

While storing information in memory is great, there comes a time your users will have to shut your application down. This means (probably) that you will need to write information to a file at some point, because you will want to store whatever data was in memory. Today, we are going to take a look at a feature built into .NET called Serialization that makes writing and reading data structures to and from a file extremely easy.

For this example, let's say I want to create a program that keeps track of all the cars my friends own. I'm going to create two objects to achieve this: Car and Owner. The Car object will store the make, model, and year of the car. The Owner object will save some information about who owns the car. Each Car object will hold a reference to an Owner object.

//information about the car
public class Car
{
   private string make;
   private string model;
   private int year;
   private Owner owner;

   public Car()
   {
   }
}

//information about the car's owner
public class Owner
{
   private string firstName;
   private string lastName;

   public Owner()
   {
   }
}

Since most of us have more than one friend, we're going to need to create a List of Car objects.

List<Car> cars = new List<Car>();

Now that we have our objects created, we're almost ready to serialize them. When I save data to files, I like to create an object specifically to hold all the things I want to serialize.

public class ObjectToSerialize
{
   private List<Car> cars;

   public List<Car> Cars
   {
      get { return this.cars; }
      set { this.cars = value; }
   }

   public ObjectToSerialize()
   {
   }
}

This class holds a reference to every object we'll want to serialize. In this case, the only thing we want to save is the list of cars. Now lets create the functions that will perform the serialization and deserialization of our object. I usually create a Serializer class to control the writing and reading to and from files.

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public class Serializer
{
   public Serializer()
   {
   }

   public void SerializeObject(string filename, ObjectToSerialize objectToSerialize)
   {
      Stream stream = File.Open(filename, FileMode.Create);
      BinaryFormatter bFormatter = new BinaryFormatter();
      bFormatter.Serialize(stream, objectToSerialize);
      stream.Close();
   }

   public ObjectToSerialize DeSerializeObject(string filename)
   {
      ObjectToSerialize objectToSerialize;
      Stream stream = File.Open(filename, FileMode.Open);
      BinaryFormatter bFormatter = new BinaryFormatter();
      objectToSerialize = (ObjectToSerialize)bFormatter.Deserialize(stream);
      stream.Close();
      return objectToSerialize;
   }
}

As you can see, the actual code required to serialize an object is relatively small and simple. At this point, however, the code will not build. Before the Serialize function can be called on ObjectToSerialize we must include the Serializable attribute and it must implement the ISerializable interface.

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable()]
public class ObjectToSerialize : ISerializable
{
   private List<Car> cars;

   public List<Car> Cars
   {
      get { return this.cars; }
      set { this.cars = value; }
   }

   public ObjectToSerialize()
   {
   }

   public ObjectToSerialize(SerializationInfo info, StreamingContext ctxt)
   {
      this.cars = (List<Car>)info.GetValue("Cars", typeof(List<Car>));
   }

   public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
   {
      info.AddValue("Cars", this.cars);
   }
}

As part of the ISerializable interface, the class must include another constructor for deserializing the object and a function GetObjectData which describes how to serialize the object. Since the Car and Owner objects are also being serialized, they will also need to implement these functions.

[Serializable()]
public class Car : ISerializable
{
   private string make;
   private string model;
   private int year;
   private Owner owner;

   public Car()
   {
   }

   public Car(SerializationInfo info, StreamingContext ctxt)
   {
      this.make = (string)info.GetValue("Make", typeof(string));
      this.model = (string)info.GetValue("Model",typeof(string));
      this.year = (string)info.GetValue("Year", typeof(int));
      this.owner = (Owner)info.GetValue("Owner", typeof(Owner));
   }

   public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
   {
      info.AddValue("Make", this.make);
      info.AddValue("Model", this.model);
      info.AddValue("Make", this.year);
      info.AddValue("Owner", this.owner);
   }
}


[Serializable()]
public class Owner : ISerializable
{
   private string firstName;
   private string lastName;

   public Owner()
   {
   }

   public Owner(SerializationInfo info, StreamingContext ctxt)
   {
      this.firstName = (string)info.GetValue("FirstName", typeof(string));
      this.lastName = (string)info.GetValue("LastName", typeof(string));
   }

   public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
   {
      info.AddValue("FirstName", this.firstName);
      info.AddValue("LastName", this.lastName);
   }
}

Now, to save the list of objects to a file, all that needs to be done is to call the Serialize and DeSerialize functions of the Serializer class.

List<Car> cars = new List<Car>();

//save the car list to a file
ObjectToSerialize objectToSerialize = new ObjectToSerialize();
objectToSerialize.Cars = cars;

Serializer serializer = new Serializer()
serializer.SerializeObject("outputFile.txt", objectToSerialize);

//the car list has been saved to outputFile.txt
//read the file back from outputFile.txt

objectToSerialize = serializer.DeSerializeObject("outputFile.txt");
cars = objectToSerialize.Cars;

That is all that's required to save and load custom C# objects to a binary file. Just like any file, it is possible for your files to become corrupted. Because of this, it is probably a good idea to add some error handling whenever output from the file is being cast to an object.

Rotik
08/27/2007 - 07:23

Hi,thax for your tutorial, it’s very helpfull. But, this is may be incorrect:

objectToSerialize =
  (SerializeObject)bFormatter.Deserialize(stream);

I think,it should be like this:

objectToSerialize =
  (ObjectToSerialize)bFormatter.Deserialize(stream);

reply

The Reddest
08/27/2007 - 08:47

You are correct. I’ve fixed the bug. Thanks for pointing it out!

reply

Mark
09/23/2007 - 22:51

I am still a bit new to serialization. Could you please post some sample code for populating your car / owner classes (ie: adding an owner and a couple of cars)?

reply

The Reddest
09/24/2007 - 12:43

Sure:

//make some cars
Car car1 = new Car();
Car car2 = new Car();

//make some owners
Owner owner1 = new Owner();
Owner owner2 = new Owner();

//set the properties - my example code doesn't have properties,
//so you'll have to add some

owner1.FirstName = "The";
owner1.LastName = "Reddest";
car1.Owner = owner1;
car1.Make = "Saturn";
car1.Model = "Sky";
car1.Year = 2007;

owner2.FirstName = "The";
owner2.LastName = "Tallest";
car2.Owner = owner2;
car2.Make = "Nissan";
car2.Model = "Maxima";
car2.Year = 2006;

//add the cars to a list
List<Car> cars = new List<Car>();
cars.Add(car1);
cars.Add(car2);

//save it to a file
ObjectToSerialize ots = new ObjectToSerialize();
ots.Cars = cars;

Serializer serializer = new Serializer()
serializer.SerializeObject("outputFile.txt", ots);

I just set my Owner and Car information to hard coded strings here, but they could just as easily be set from the Text property of a TextBox. The major difference here is that I'm assuming there are properties defined for each variable in the Car and Owner objects. I didn't define them in the tutorial to keep the example code smaller. I hope this helps.

reply

Chris
10/18/2007 - 18:40

You should put this tutorial in the MSDN ISerializable documentation user contributions. Their example code is totally useless for ISerializable noobs.

reply

Chris
10/18/2007 - 18:55

Sorry if you took that as spam.

reply

The Fattest
10/18/2007 - 19:14

No problem Chris, you’re fine - we wouldn’t let it come through if it was spam.

reply

Aaron
11/11/2007 - 21:59

Would this method of saving data to a file be suitable for large amounts of data, say like a few hundred ints, strings, chars, arraylists, and hashtables. Eg; if in the above you had a couple hundred cars and owners, would this still be an efficient way to do this, or is there a beter way to save object data to a file.

cheers
Aaron.

reply

The Reddest
11/11/2007 - 22:17

I think the above example would work well for up to a few thousand cars. After that point the time required to deserialize might become too long. I haven’t done any testing to determine where the cutoff point is, so I can’t answer for sure. I would say this method would work fine for any amount of data you’d comfortably want to hold in memory at one time (a few megabytes). Any more than that, and you should probably start thinking about a database.

reply

Scott
12/22/2007 - 11:33

That was a great example and I was wondering if your readers would like to spice it up a little more by making your Serializer class into a "helper" or "utility" class... I named it FileSerializer for this example.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

namespace MyUtilities
{
  ///
  /// General FileSerializer - need to cast from ISerializable :-(
  ///
  public static class FileSerializer
  {
    public static void Serialize( string filename,
        ISerializable objectToSerialize )
    {
      Stream stream = File.Open(filename, FileMode.Create);
      BinaryFormatter bFormatter = new BinaryFormatter();
      bFormatter.Serialize(stream, objectToSerialize);
      stream.Close();
    }

    public static ISerializable DeSerialize( string filename )
    {
      ISerializable objectToSerialize;
      Stream stream = File.Open(filename, FileMode.Open);
      BinaryFormatter bFormatter = new BinaryFormatter();
      objectToSerialize =
          (ISerializable)bFormatter.Deserialize(stream);
      stream.Close();
      return objectToSerialize;
    }
  }
}

Now you could take this one step further and use generics.

namespace MyUtilities
{
  ///
  /// FileSerializer, based on generics - no more casting :-)
  ///
  public static class FileSerializer
    where T : class, ISerializable
  {
    public static void Serialize( string filename,
        T objectToSerialize )
    {
      Stream stream = File.Open(filename, FileMode.Create);
      BinaryFormatter bFormatter = new BinaryFormatter();
      bFormatter.Serialize(stream, objectToSerialize);
      stream.Close();
    }

    public static T DeSerialize( string filename )
    {
      T objectToSerialize;
      Stream stream = File.Open(filename, FileMode.Open);
      BinaryFormatter bFormatter = new BinaryFormatter();
      objectToSerialize = (T)bFormatter.Deserialize(stream);
      stream.Close();
      return objectToSerialize;
    }
  }
}

And here is a slightly revised version of the classes we are using in this example (note, I've dropped the use of a separate serializing class and introduced a way to add serialization to a collection (note: this can be extended to support dictionaries too).

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace MyUtilities
{
  [Serializable]
  public class Car : ISerializable
  {
    private readonly string _make;
    private readonly string _model;
    private readonly int _year;

    //this is variable and can change
    private Owner _owner = null;

    //default ctor not valid -
    //we want to enforce initializing our data
    private Car() { }
   
    public Car( string make, string model, int year )
    {
      _make = make;
      _model = model;
      _year = year;
    }

    public Owner Owner
    {
      get { return _owner; }
      set { _owner = value; }
    }
   
    public string Make
    { get { return _make; } }
   
    public string Model
    { get { return _model; } }

    public int Year
    { get { return _year; } }

    //note: this is private to control access;
    //the serializer can still access this constructor
    private Car( SerializationInfo info, StreamingContext ctxt )
    {
      _make = info.GetString("Make");
      _model = info.GetString("Model");
      _year = info.GetInt32("Year");
      _owner = (Owner)info.GetValue("Owner", typeof(Owner));
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags =
       SecurityPermissionFlag.SerializationFormatter)]
    public void GetObjectData( SerializationInfo info,
       StreamingContext ctxt )
    {
      info.AddValue("Make", this._make);
      info.AddValue("Model", this._model);
      info.AddValue("Year", this._year);
      info.AddValue("Owner", this._owner, typeof(Owner));
    }
  }
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace MyUtilities
{
  [Serializable]
  public class Owner : ISerializable
  {
    private string _firstName = string.Empty;
    private string _lastName = string.Empty;

    public Owner() { }

    public Owner(string firstName, string lastName)
    {
      _firstName = firstName;
      _lastName = lastName;
    }

    //note: this is private to control access;
    //the serializer can still access this constructor
    private Owner( SerializationInfo info, StreamingContext ctxt )
    {
      _firstName = info.GetString("FirstName");
      _lastName = info.GetString("LastName");
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags =
        SecurityPermissionFlag.SerializationFormatter)]
    public void GetObjectData( SerializationInfo info,
        StreamingContext ctxt )
    {
      info.AddValue("FirstName", this._firstName);
      info.AddValue("LastName", this._lastName);
    }
  }
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace MyUtilities
{
  //note: would be better to contain the List of cars,
  //than derive from it
  [Serializable]
  public sealed class Cars : List, ISerializable
  {
    public Cars()
      : base()
    { }
   
    public Cars( int capacitiy )
      : base(capacitiy)
    {  }

    public Cars( IEnumerable steps )
      : base(steps)
    { }

    private Cars( SerializationInfo info,
        StreamingContext context )
    {
      int count = info.GetInt32("NumOfCars");
      for (int ix = 0; ix < count; ix++)
      {
        string key = "Car_" + ix.ToString();
        Car car = (Car)info.GetValue(key, typeof(Car));
        this.Add(car);
      }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags =
        SecurityPermissionFlag.SerializationFormatter)]
    public void GetObjectData( SerializationInfo info,
        StreamingContext context )
    {
      info.AddValue("NumOfCars", this.Count);
      int ix = 0;
      foreach (Car car in this)
      {
        string key = "Car_" + ix.ToString();
        info.AddValue(key, car, typeof(Car));
        ix++;
      }
    }
  }
}

And the example:

public void RunExample()
{
  Car car1 = new Car("Ford", "Mustang GT", 2007);
  Car car2 = new Car("Dodge", "Viper", 2006);
  car1.Owner = new Owner("Rich", "Guy");
  car2.Owner = new Owner("Very", "RichGuy");
 
  //save cars individually
  //note: type-safe as Car
  FileSerializer.Serialize(@"C:\Car1.dat", car1);
  //note: implicit casting to ISerializable
  FileSerializer<Car>.Serialize(@"C:\Car2.dat", car2);

  //save as a collection
  Cars cars = new Cars();
  cars.Add(car1);
  cars.Add(car2);
  //note: type-safe as Cars
  FileSerializer.Serialize(@"C:\Cars.dat", cars);

  //now read them back in (note, you can use either FileSerializer
  //note: casting required
  Car savedCar1 = (Car)FileSerializer.DeSerialize(@"C:\Car1.dat");
  //note: no casting required  
  Car savedCar2 = FileSerializer<Car>.DeSerialize(@"C:\Car2.dat");

  //and for the collection…
  Cars savedCars =
    FileSerializer<Cars>.DeSerialize(@"C:\Cars.dat");
}

I've introduced a few new things and hopefully it helps.

-Scott

reply

Scott
12/22/2007 - 11:38

Oh ya, one more thing - you may find it necessary to specify security settings on the GetObjectData.
Example:

using System.Security.Permissions;

[SecurityPermission(SecurityAction.LinkDemand, Flags =
  SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData( SerializationInfo info,
  StreamingContext context )
{
  //check info before using
  if (info == null)
    throw new System.ArgumentNullException(”info”);
}

reply

nhovehn
12/31/2007 - 03:00

I've tried ur code and had error when I DeSerialize Object:

objectToSerialize = serializer.DeSerializeObject(sFilename);

Error when execute command in DeSerializeObject(...):

objectToSerialize =
  (ObjectToSerialize)bFormatter.Deserialize(stream);

Error:

System.Reflection.TargetInvocationException was unhandled
Message="Exception has been thrown by the target of an invocation."
Source="mscorlib"
StackTrace:
at System.RuntimeMethodHandle._SerializationInvoke(Object target,
  SignatureStruct&amp; declaringTypeSig, SerializationInfo info,
  StreamingContext context)
at System.RuntimeMethodHandle.SerializationInvoke(Object target,
  SignatureStruct declaringTypeSig, SerializationInfo info,
  StreamingContext context)
at System.Reflection.RuntimeConstructorInfo.SerializationInvoke(
  Object target, SerializationInfo info, StreamingContext context)
at System.Runtime.Serialization.ObjectManager.
  CompleteISerializableObject(Object obj, SerializationInfo info,
  StreamingContext context)
at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(
  ObjectHolder holder)
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.
  Deserialize(HeaderHandler handler, __BinaryParser serParser,
  Boolean fCheck, Boolean isCrossAppDomain,
  IMethodCallMessage methodCallMessage)
at  System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.
  Deserialize(Stream serializationStream, HeaderHandler handler,
  Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage
  methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.
  Deserialize(Stream serializationStream)
at VLAuto4Me.Serializer.DeSerializeObject(String sFilename) in
  E:\MyGames\Tools\VLTK\SourceCodes\VLAuto4Me\VLAuto4Me\
  Serialize.cs:line 36
at VLAuto4Me.AutoLogin4Me.cmdSaveList_Click(Object sender,
  EventArgs e) in E:\MyGames\Tools\VLTK\SourceCodes\
  VLAuto4Me\VLAuto4Me\AutoLogin4Me.cs:line 41
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp
  (Message&amp; m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message&amp; m)
at System.Windows.Forms.ButtonBase.WndProc(Message&amp; m)
at System.Windows.Forms.Button.WndProc(Message&amp; m)
at System.Windows.Forms.Control.ControlNativeWindow.
  OnMessage(Message&amp; m)
at System.Windows.Forms.Control.ControlNativeWindow.
  WndProc(Message&amp; m)
at System.Windows.Forms.NativeWindow.DebuggableCallback
  (IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW
  (MSG&amp; msg)
at System.Windows.Forms.Application.ComponentManager.System.
  Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.
  FPushMessageLoop(Int32 dwComponentID, Int32 reason,
  Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.
  RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop
  (Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at VLAuto4Me.Program.Main() in
  E:\MyGames\Tools\VLTK\SourceCodes\VLAuto4Me\VLAuto4Me\
  Program.cs:line 15
at System.AppDomain.nExecuteAssembly(Assembly assembly,
  String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence
  assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(
  ExecutionContext executionContext, ContextCallback callback,
  Object state)
at System.Threading.ThreadHelper.ThreadStart()

???

reply

Scott
12/31/2007 - 17:40

Since I last posted, I’ve refined my utility class further; hope this helps

public static class FileSerializer
{
  public static void Serialize( string filename,
    object objectToSerialize )
  {
    if(objectToSerialize == null)
      throw new ArgumentNullException(
        "objectToSerialize cannot be null");

    Stream stream = null;

    try
    {
      stream = File.Open(
        PathHelper.BuildPath(filename), FileMode.Create);
      BinaryFormatter bFormatter = new BinaryFormatter();
      bFormatter.Serialize(stream, objectToSerialize);
    }
    finally
    {
      if (stream != null)
        stream.Close();
    }
  }

  public static T Deserialize <T>( string filename )
  {
    T objectToSerialize;
    Stream stream = null;

    try
    {
      stream = File.Open(
          PathHelper.BuildPath(filename), FileMode.Open);
      BinaryFormatter bFormatter = new BinaryFormatter();
      objectToSerialize = (T)bFormatter.Deserialize(stream);
    }
    finally
    {
      if (stream != null)
      stream.Close();
    }

    return objectToSerialize;
  }
}

I’ll re-paste my example too…

reply

Scott
12/31/2007 - 18:11

Note: remove the PathHelper.BuildPath too, these are some other utilities that I wrote; just leave the filename.

reply

Scott
12/31/2007 - 17:54

Here is a revised example:

public void RunExample()
{
  Car car1 = new Car("Ford", "Mustang GT", 2007);
  Car car2 = new Car("Dodge", "Viper", 2006);
  car1.Owner = new Owner("Rich", "Guy");
  car2.Owner = new Owner("Very", "RichGuy");

  //save cars individually
  FileSerializer.Serialize(@"C:\Car1.dat", car1);

  //save as a collection
  Cars cars = new Cars();
  cars.Add(car1);
  cars.Add(car2);
  FileSerializer.Serialize(@"C:\Cars.dat", cars);

  //now read them back in
  Car savedCar1 = FileSerializer<Car>.DeSerialize(@"C:\Car1.dat");
  Car savedCar2 = FileSerializer<Car>.DeSerialize(@"C:\Car2.dat");
 
  //and for the collection…
  Cars savedCars = FileSerializer<Cars>.DeSerialize(@"C:\Cars.dat");
}

I’ll re-post the collection example next…

reply

Scott
12/31/2007 - 17:56

Oh, my bad again… move the <Car> to after the DeSerialize and before the (

reply

nhovehn
01/02/2008 - 10:25

Error when DeSerializer a file:

Cars savedCars = FileSerializer<Cars>.DeSerialize(@”C:\Cars.dat);

Error here:
The non-generic type ‘Project1.FileSerializer’ cannot be used with type arguments Form1.cs

reply

Scott
01/02/2008 - 13:29

Sorry, that was due to a typo on my part. Should be…

FileSerializer.DeSerialize<Cars>

reply

nhovehn
01/03/2008 - 00:04

Hi, :(
I've tried so still error when DeSerializer:

objectToSerialize = (T)bFormatter.Deserialize(stream);

Error:
Exception has been thrown by the target of an invocation.

reply

Scott
01/03/2008 - 06:49

Double check the Cars class, making sure that the following method is correct:

private Cars(SerializationInfo info,
    StreamingContext context)

Did you notice there was a &lt;? you should replace it with a less-than operator, if you have not already done so.

if you still have problems, try stepping through it and see when it throws an exception - you could add a try\catch block in there to help too.

If you still have problems or you are getting the same error about a non-generic type, make sure that all three statements are:
FileSerializer.DeSerialize <Cars>

reply

nhovehn
01/04/2008 - 00:29

Hi,
I've checked again and everything is the same with your code.
Of course I see a &lt; and understand that is less-than quote :(.

And I try cacth the statement:

objectToSerialize = (T)bFormatter.Deserialize(stream);

So cacth:

try
{
  stream = File.Open(filename, FileMode.Open);
  BinaryFormatter bFormatter = new BinaryFormatter();
  objectToSerialize = (T)bFormatter.Deserialize(stream);
}
catch (SerializationException)
{
  MessageBox.Show(
    "The application failed to retrieve the inventory");
}
finally
{
  if (stream != null)
    stream.Close();
}

objectToSerialize always gets null value

reply

Scott
01/05/2008 - 02:40

Sorry that you are have problems…
Since I can not duplicate your problem, I need some more info.
Can you replace the catch with this…

T objectToSerialize = default(T);
Stream stream = null;

try
{
  stream = File.Open(filename, FileMode.Open);
  BinaryFormatter bFormatter = new BinaryFormatter();
  objectToSerialize = (T)bFormatter.Deserialize(stream);
}
catch (Exception err)
{
  MessageBox.Show(
    "The application failed to retrieve the inventory"
    + err.Message);
}
finally
{
  if (stream != null)
    stream.Close();
}
return objectToSerialize;

and let me know what the error message is, thanks.

reply

nhovehn
01/07/2008 - 00:49

OK,
And that error is here:
(Value of ex.Message)
Exception has been thrown by the target of an invocation.
I don’t know why that error occur with my progs.
Thanks for your helping!

reply

Scott
01/09/2008 - 05:15

Humm, not much help.

I've decided to post this at CodeProject
http://www.codeproject.com/KB/cs/SerializeUtility.aspx

You can download the sample code there and see if you still have the problem.

I included a link back to this site, hope you guys don't mind!

-Scott

reply

The Reddest
01/09/2008 - 10:44

We definitely don’t mind, Scott. Thanks for the contributions.

reply

Scott
01/09/2008 - 17:03

Hi nhovehn,
I just found what your problem might be. Your error should be getting thrown at this line of code in the RunExample method

Car savedCar2 =
  FileSerializer.Deserialize<Car>(@”C:\Car2.dat);

the problem is the file is missing because we are missing the part to serialize it

FileSerializer.Serialize(@”C:\Car2.dat”, car2);

reply

nhovehn
01/10/2008 - 11:37

Thanks for all,
I guess my error that: I serialize a car, but deserialize a list cars
=> Error, :(

reply

Scott
12/31/2007 - 17:58

Here is the Cars class

//note: would be better to contain the List of cars, //than derive from it
[Serializable]
public sealed class Cars : List <Car>, ISerializable
{
  public Cars()
    : base()
  { }

  public Cars( int capacitiy )
    : base(capacitiy)
  { }
 
  public Cars( IEnumerable<Car> steps )
    : base(steps)
  { }

  private Cars( SerializationInfo info, StreamingContext context )
  {
    int count = info.GetInt32("NumOfCars");
    for (int ix = 0; ix < count; ix++)
    {
      string key = "Car_" + ix.ToString();
      Car car = (Car)info.GetValue(key, typeof(Car));
      this.Add(car);
    }
  }

  [SecurityPermission(SecurityAction.LinkDemand, Flags =
    SecurityPermissionFlag.SerializationFormatter)]
  public void GetObjectData( SerializationInfo info,
    StreamingContext context )
  {
    info.AddValue("NumOfCars", this.Count);
    int ix = 0;
    foreach (Car car in this)
    {
      string key = "Car_" + ix.ToString();
      info.AddValue(key, car, typeof(Car));
      ix++;
    }
  }
}

reply

Scott
12/31/2007 - 18:03

Of course I’ve ommited error checking and proper handling of errors to shorten the example up.

You should add try and catch to the Utility class and could add checking that the class supports serialization. Some errors that could occur: file missing, can not deserialize - was serialized with different content or not of the type T, etc.

reply

Leven
01/25/2008 - 03:55

Scott, can you send me a example proyect? Thanks :D

reply

Scott
02/15/2008 - 09:28

Sample Project can be found at
http://www.codeproject.com/KB/cs/SerializeUtility.aspx

reply

David
01/09/2008 - 16:49

Hm, I think I get the basic concept. I’m assuming earlier tutorials mentioned need of some sort of libraries, yes? Or a specific compiler mentioned on this website somewhere? Sorry, I’m new to this.

reply

Scott
01/09/2008 - 17:52

Hi David

The latest code did not paste well, I’ve included a sample download at Code Project.

The current code is in C# with .NET 2.0. I’m currently using Visual Studio 2005, but you should be able to use any C# NET 2.0 compiler.

-Scott

reply

Lori
02/27/2008 - 13:37

I have never programed before, so I was wondering if you could tell me what to do with my problem. I use HP Image Zone Plus to edit my pictures. I now get the error: Parameter cannot be null. Parameter:serializationStream. I tried uninstalling & reinstalling HP ImageZone, and used HP Support, but was told this was an internal problem. What should I do? Also - anyone know of the best program to use to edit photos?? Or a way to minimize lots of photos at once for email or website purposes instead of one by one? Thanks, Lori

reply

The Reddest
02/27/2008 - 16:56

Well Lori, unfortunately I don't have any insight into the error you're seeing. It sounds like a problem with HP Image Zone or your operating system. You could try reinstalling the .NET Framework.

As far as image tools goes, the best one out there by far is Adobe Photoshop. It's a little expensive, but nothing can compare to it in terms of power and features. If you want a free one that will do all of your typical editing needs, go with Paint .NET.

Photoshop will batch edit images for you. We actually have a tutorial here on the blog that demonstrates how to do that. I don't know whether or not Paint .NET has batch processing, but I don't think it does.

reply

Ethan
07/13/2008 - 13:55

The code is misleading. Not everyone needs to implement the ISerializable interface.

This is from MSDN:"If a class needs to control its serialization process, it can implement the ISerializable interface."

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx

reply

Ethan
07/13/2008 - 13:56

You said "must" here. "Before the Serialize function can be called on ObjectToSerialize we must include the Serializable attribute and it must implement the ISerializable interface."
As I said it is optional.

reply

The Fattest
07/13/2008 - 16:06

I would say it is recommended for good coding practice.

reply

Gaurav Arora
07/20/2008 - 03:47

A newbee to .NET, Please help

public Car(SerializationInfo info,StreamingContext ctxt)

function gives a type conversion error on this line

this.owner = (string)info.GetValue("Owner", typeof(Owner));

Error
Error 1: Cannot implicitly convert type 'string' to
  'System.Collections.Generic.List'
D:\dotnetpractice\practice\ConsoleApplication1\
  ConsoleApplication1\Car.cs

reply

The Reddest
07/20/2008 - 08:30

Sorry about that. There was a typo in the post's code. this.owner is of type Owner, and the code was attempting to cast it to a string. Your line needs to be replaced with:

this.owner = (Owner)info.GetValue("Owner", typeof(Owner));

Thanks for catching that!

reply

Gregory
07/27/2008 - 07:11

Hello everyone,

Does anybody have an experience recording C# objects into HDF5 file format? Does anybody know about recording arbitrary C# objects in HDF5 format?

Thanks a lot,
-Gregory

reply

Joe Blue
08/13/2008 - 09:17

There is a small mistake in the original post, which may be the reason people are having problems with this code.

The correction appears below.
Thanks,
JB

public Serializer()
{
  //add this next line here
  ObjectToSerialize objectToSerialize = new ObjectToSerialize();
}

public ObjectToSerialize DeSerializeObject(string filename)
{
  //this line should be commented out,
  //as it is a local variable only, not what is needed
  //ObjectToSerialize objectToSerialize;
}

reply

Joe Blue
08/13/2008 - 09:22

One more minor thing–for your code to even compile, you must of course place a declaration of objectToSerialize before you instantiate it, as here:

class Serializer
{
  //add this of course…
  ObjectToSerialize objectToSerialize;

  //and the rest as before…
  public Serializer()
  {
    //add this next line here
    ObjectToSerialize objectToSerialize =
        new ObjectToSerialize();
  }

  public ObjectToSerialize DeSerializeObject(string filename)
  {
    //this line should be commented out,
    //as it is a local variable only, not what is needed
    //ObjectToSerialize objectToSerialize;
  }
}

reply

The Reddest
08/13/2008 - 09:55

I don't necessarily agree with your suggestions.

There's no reason for the Serializer class to contain a private member for the ObjectToSerialize object. There's no functions or properties to retrieve it and it's not used anywhere else within the class. This is why I chose to simply declare and populate one on the fly within the DeserializeObject function. Unlike C++, that object will not be destructed when it falls out of scope.

In your first example, you'd actually be creating an extra ObjectToSerialize object for no reason when the Serializer class is constructed. The reference would simply be overwritten by a new ObjectToSerialize every time bFormatter.Deserialize(stream) is called.

reply

Niks
10/06/2008 - 07:15

hii, i like the article you wrote. i also wrote an article on Serialization here : http://kaniks.blogspot.com
feel free to post your comments

thanks
cheers :)

reply

Ashish
11/08/2008 - 15:29

In this scenario, you are storing List in file and later retreiving it.

I have a case in which I get one object at a time (when someone publishes it) and i have to store it into a file that time. When next object comes (same type), I then send it to same file. So, I am not saving a List but lot of T's in same file.

Now, how do I retreive it? When I keep FileStream.Position = 0, I can get first object by Deserialize method, but how do I get to second object and so on in my file.

Please Help. Thanks in Advance.

reply

The Reddest
11/08/2008 - 15:49

It almost sounds like you're trying to use a file in the same way you would a database. So in this scenario, I think a database of some sort would probably be the best answer. SQLite offers a completely free, self-contained database that might work for your needs. There are also C# libraries available for it.

If you're still wanting to use serialization, I would not recommend serializing each object to the end of the file. I would recommend serializing a List of objects and each time you want to add a new one, deserialize the List, add your object to it, then re-serialize the List back to the file. When you want to retrieve it, just deserialize the List and find your object.

This would be the simplest technique, but it would become far too expensive if you're wanting to store several thousands of objects. However, if that were the case, I would then strongly recommend a database.

reply

Kristof otai
11/27/2008 - 12:10

Thank you for this comprehensive tutorial. It really helped me.

Kristof, from Hungary

reply

Chris
12/02/2008 - 11:27

Nice examples, however, I would recommend that you properly DISPOSE of your objects after you're through with them.

IMHO - an example is no excuse for sloppy practices ...

reply

The Reddest
12/02/2008 - 14:57

Chris, you're 100% correct. I should have surrounded the Stream creation with a using statement to ensure it's disposed. Thanks for pointing it out.

reply

Xilinxxx
01/25/2009 - 09:11

Thank you. You helped me a lot.

reply

Gert-Jan
04/27/2009 - 16:39

Hi, you can also store directly in, and retrieve multiple objects from a single file storage, using NFileStorage, see NFileStorage on CodePlex (100% sources included, all managed c#)

reply

brent
06/04/2009 - 06:48

Hi, I've tried the original tutorial code. I seem to have a problem during deserialization.

I've collected all the tutorial source code snippets together to create the enclosed console based application.

The application provides 3 commands i,s,l

To create the scenario run code.exe and type the following,
i ..this creates 2 entries
i ..this creates 2+2 entries
s .. saves the binary data into data3.txt
q .. quits the console application

now run code.exe a second time
l .. this loads data3.txt

At this point I would expect to see 4 car entries but the console debug indicates that the cars list has zero entries after deserialization. Something obvious is missing in the code but I am a novice to c# so I'm stumped.

The source is contained in a single file called code.cs - see below.

I'm using the microsoft c# command line compiler, csc. So, from the dos command line,
to compile, type:- csc code.cs
to run, type:- code.exe

 

//code.cs

//based on c# tutorial - serialize objects to a file by The Reddest
//goto http://www.switchonthecode.com/tutorials/csharp-tutorial-serialize-objects-to-a-file


//Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mydata;
using serializers;

//extra bits to make the single file version compile
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace filer3
{
    class Program
    {

        static void setup(List<TCar> list, int n)
        {
            string N = Convert.ToString(n);
            TCar car1 = new TCar();
            TOwner owner1 = new TOwner();
            list.Add(car1);

            car1.owner = owner1;
            car1.make = "Saturn" + N;
            car1.model = "Sky" + N;
            car1.year = 2007 + n;
            owner1.firstName = "The" + N;
            owner1.lastName = "Reddest" + N;

            TCar car2 = new TCar();
            TOwner owner2 = new TOwner();
            list.Add(car2);

            owner2.firstName = "The" + N;
            owner2.lastName = "Tallest" + N;
            car2.owner = owner2;
            car2.make = "Nissan" + N;
            car2.model = "Maxima" + N;
            car2.year = 2006 + n;
        }


        static void Main(string[] args)
        {
            int j = 0;
            List<TCar> cars = new List<TCar>();
            TDocument document = new TDocument();
            TFiler filer = new TFiler();
            TCar xx;
            string fname = "data3.txt";
            document.Cars = cars;

            while (1 == 1)
            {

                Console.Write("enter command >");
                string line = Console.ReadLine();
                if (line == "i")
                {
                    Console.WriteLine("initialised");
                    setup(document.Cars, j++);
                }
                if (line == "l")
                {
                    Console.WriteLine("loading");
                    filer.DeSerializeObject(fname);
                }
                if (line == "s")
                {
                    Console.WriteLine("saving");
                    filer.SerializeObject(fname, document);
                }
                if (line == "q")
                {
                    Console.WriteLine("quit");
                    break;
                }


                Console.WriteLine("Entries =" + Convert.ToString(document.Cars.Count));
                for (int n = 0; n < document.Cars.Count; n++)
                {
                    xx = document.Cars.ElementAt(n);
                    Console.Write(Convert.ToString(n) + "  ");
                    Console.Write(xx.make + ",");
                    Console.Write(xx.model + ",");
                    Console.WriteLine(xx.year);
                }
            }
        }
    }
}
//TCar.cs
//using System;
//using System.IO;
//using System.Runtime.Serialization;

namespace mydata
{
    [Serializable()]
    public class TCar:ISerializable
    {
        public string make;
        public string model;
        public int year;
        public TOwner owner;

        public TCar()
        {
        }        
        public TCar(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("TCAR deserialising");
            this.make = (string)info.GetValue("Make", typeof(string));
            this.model = (string)info.GetValue("Model",typeof(string));
            this.year = (int)info.GetValue("Year", typeof(int));
            this.owner = (TOwner)info.GetValue("Owner", typeof(TOwner));
        }
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("TCAR serialising");
            info.AddValue("Make", this.make);
            info.AddValue("Model", this.model);
            info.AddValue("Year", this.year);
            info.AddValue("Owner", this.owner);            
        }      
    }
}
//TDocument.cs
//using System;
//using System.IO;
//using System.Runtime.Serialization;
//using System.Collections;
//using System.Collections.Generic;
//using System.Runtime.Serialization.Formatters.Binary;
//using mydata;


namespace mydata
{

    [Serializable()]
    // Top level data object all other data types are encapsulated by this class
    public class TDocument : ISerializable
    {
        private List<TCar> cars;

        public  List<TCar> Cars
        {
            get { return this.cars; }
            set { this.cars = value; }
        }

        public TDocument()
        {
        }

        public TDocument(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("document deserialising");
            this.cars = (List<TCar>)info.GetValue("Cars", typeof(List<TCar>));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("document serialising");
            info.AddValue("Cars", this.cars);
        }
    }
}

//TOwner.cs
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;

//using System.IO;
//using System.Runtime.Serialization;
//using System.Runtime.Serialization.Formatters.Binary;


namespace mydata
{
    [Serializable()]
    public class TOwner : ISerializable
    {
        public string firstName;
        public string lastName;

        public TOwner()
        {
        }
        public TOwner(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("TOWNER deserialising");
            this.firstName = (string)info.GetValue("FirstName", typeof(string));
            this.lastName =  (string)info.GetValue("LastName",  typeof(string));
        }
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            Console.WriteLine("TOWNER serialising");
            info.AddValue("FirstName", this.firstName);
            info.AddValue("LastName", this.lastName);
        }
    }
}
//TSerialiser_1.cs
//using System;
//using System.IO;
//using System.Runtime.Serialization;
//using System.Runtime.Serialization.Formatters.Binary;
//using mydata;

namespace serializers
{
    public class TFiler
    {
        //Version 1 serialiser, instance specific, so a bit of a pain

        public void SerializeObject(string filename, TDocument document)
        {
            Stream stream = File.Open(filename, FileMode.Create);  
            BinaryFormatter bFormatter = new BinaryFormatter();
            bFormatter.Serialize(stream, document);
            stream.Close();
        }

        public TDocument DeSerializeObject(string filename)        
        {
            TDocument document;
            Stream stream = File.Open(filename, FileMode.Open);
            BinaryFormatter bFormatter = new BinaryFormatter();
            document = (TDocument)bFormatter.Deserialize(stream);
            stream.Close();
            return document;
        }
    }
}

reply

Saleem
06/16/2009 - 02:09

Brent,
Your code actually works except for a very minor mistake.
You are inserting data OK, saving it OK. When you read it back, the function returns a document. You are not using that document that is returned. Look at code snippet below. I have commented out the line you had.

if (line.ToLower().StartsWith("l"))
{
    Console.WriteLine("loading");
    // Error filer.DeSerializeObject(fname);
    document = filer.DeSerializeObject(fname);
}

reply

Bonjo
07/08/2009 - 05:25

Can you please explain why we need to implemet ISerializable and realize methods:

public ObjectToSerialize(SerializationInfo info, StreamingContext ctxt);
public void GetObjectData(SerializationInfo info, StreamingContext ctxt);

Because it seems the example succesfully works without this explicit serialization, I think attribute [Serializable()] is enough to make it works.

reply

MSGhazi
08/06/2009 - 15:17

This was an excellent tutorial. Step by step, clear, organized instructions. Thank you very much!

reply

Frank
09/02/2009 - 05:10

Hi, is there anyway to serialize a .pdf file to a binary format and save it into an SQL database?

reply

Kate
09/08/2009 - 08:36

This is a really good example which I followed to create a file and read it back. However, I had to change one of the variables in the object to a double. Now it creates the file happily, but crashes when it read it back. Is there a problem with serialising doubles?
Any advice much appreciated.

reply

Kate
09/09/2009 - 04:14

Stupid mistake now corrected - code works well for doubles. Sorry to have troubled you.

reply

Anonymous
10/29/2009 - 08:45

You need to give examples of serializing to XML in memory. There are plenty of scenarios where you'd want to do this.

http://www.dotnetjohn.com/articles.aspx?articleid=173

reply

SRSavior
11/16/2009 - 22:57

Hi,

The tutorial was very useful and educational, but I'm having a problem:

When I serialize, it seems to go through fine, although I suppose it's hard to know that for sure. However, when I try to deserialize, I get:

System.Reflection.TargetInvocationException was unhandled
Message="Exception has been thrown by the target of an invocation."

I'm not sure if this is because I'm also trying to serialize/deserialize sub-objects, but I followed the tutorial as much as I could as it pertains to my code.

I have a List, which contains string fields, but one of the fields in each object is a list of a sub-object, which also has two string fields within it. So, I used the AddValue and GetValue for the sub-object in the same way as described. Any ideas? Sorry I couldn't be clearer.

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors