Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Binary Serialization for Objects with Private Attributes

5.00/5 (4 votes)
6 Feb 2023CPOL3 min read 5K   48  
This work aims to show how to do binary serialization and deserialization of an array of objects in C# with .NET 7.
We are going to write some extension methods for the BinaryWriter and BinaryReader classes, in order to allow for easy writing, and reading, the objects from disk.

Image 1

Introduction

The BinaryFormatter class is now considered obsolete, not safe and therefore it should not be used anymore (Deserialization risks in use of BinaryFormatter and related types - 28/11/2022).

In order to do binary serialization of objects with private attributes, we can use the BinaryWriter and BinaryReader classes.

These classes require the programmer’s work to write any single operation for writing and reading data from disk.

This work is aimed at showing how to do binary serialization and deserialization of an array of objects in C# with .NET7.

We are going to write some extension methods for the BinaryWriter and BinaryReader classes, in order to allow for easy writing, and reading, the objects from disk.

The extension methods are a means to add new functionalities to an existing class of the framework, without the need to recompile its source code.

The extension methods that we will write for the BinaryWriter and BinaryReader classes should be contained in static classes.

The Article Body

The class diagram of our example is shown in the above picture.

The School class contains an array of Persons, which may have an Auto.

The School class has the SaveData() and LoadData() methods; they serve for, respectively, the serialization and the deserialization of the array of persons.

The array is managed in a semi-dynamic way: it has a fixed rather big dimension and is partially filled with objects.

The nPeople attribute represents the number of objects actually present in the array and, at the same time, it indicates the first free position in the array.

Indeed, the nPeople are loaded in the cells from 0 to nPeople-1.

All the attributes are private, therefore we need some accessor methods for reading and setting their values.

The classes provided for XML and JSON serialization act only on the public attributes of the classes, therefore we choose the binary serialization and use the BinaryWriter and BinaryReader classes.

The BinaryWriter has the Write() method which allows to write to disk only primitive data, such as strings, chars, integer numbers, double precision floating point numbers and booleans.

We write some extension methods in order to write objects of type Auto and Person:

C#
// extension methods for serialization

public static void WriteAuto(this BinaryWriter bw, Auto a)
{
   bw.Write(a.GetBrand());
   bw.Write(a.GetModel());
}

public static void WritePersona(this BinaryWriter bw, Person p)
{
	bw.Write(p.GetName());
	bw.Write(p.GetAge());
	Auto a = p.GetAge(); 
	if (a != null)      // check the presence of an auto
	{
	   bw.Write(true);  // there is an auto
	   bw.WriteAuto(a);
	}
	else
	{
	   bw.Write(false); // no auto
	}
}

It is worth noting that, as it is not guaranteed that all people have a car, it is necessary to check the presence of an auto and to write to disk a boolean value that states the presence or the absence of it.

The BinaryReader has only such methods as ReadString(), ReadChar(), ReadInt32(), ReadDouble(), ReadBoolean() for reading data of a primitive type.

The extension methods used for reading objects of type Auto and Person are the following:

C#
public static Auto ReadAuto(this BinaryReader br)
{
   string brand = br.ReadString();
   string model = br.ReadString();
   return new Auto(brand, model);
}

public static Person ReadPersona(this BinaryReader br)
{
   string name = br.ReadString();
   int age = br.ReadInt32();
   bool hasAuto = br.ReadBoolean();
   if (hasAuto)
   {
	Auto a = br.ReadAuto();
	return new Person(name, age, a);
   }
   else
   {
      return new Person(name, age);
   }
}

The above methods are placed in some static classes.

In the School class, there are the methods aimed at serializing and deserializing the array of persons with all their contents:

C#
public void SaveData()
{
    // serialization
    FileStream file = File.Create("data.bin");
    BinaryWriter bw = new BinaryWriter(file);
    bw.Write(nPeople);
    for (int i = 0; i < nPeople; i++)
    {
         bw.WritePerson(array[i]);
    }
    file.Close();
}

public void LoadData()
{
    // deserialization
    FileStream file = File.OpenRead("data.bin");
    BinaryReader br = new BinaryReader(file);
    nPeople = br.ReadInt32();
    for (int i = 0; i < nPeople; i++)
    {
       array[i] = br.ReadPerson();
    }
    file.Close();
}

It is worth noting that, in addition to the bare array, it is necessary to serialize the value of nPeople too, because it gives the real number of people placed in the array.

The Main() method of the Program class allows us to test the operations:

C#
// some objects for the test
Auto a1 = new Auto("opel", "mokka");
Person p1 = new Person("gianni", 20, a1);
Auto a2 = new Auto("mercedes", "gla");
Person p2 = new Person("luca", 30, a2);
Person p3 = new Person("piero", 40); 

// creation of the school and filling
School school = new School();
school.AddPerson(p1);
school.AddPerson(p2);
school.AddPerson(p3);

// saving to disk
school.SaveData();

// resetting the school and loading data
school = new School();
school.LoadData();

// verification
int n = school.GetNPeople();
for (int i = 0; i < n; i++)
{
    Person p = school.GetPerson(i); 
    Console.WriteLine(p);
}

We obtain the following output:

gianni 20 opel mokka
luca 30 mercedes gla
piero 40

The complete code for the project is attached to this article and can be downloaded.

Conclusion and Points of Interest

In this article, we have shown a simple way for replacing the functionalities of the BinaryFormatter class, whose usage is now forbidden.

We only have to add a couple of extension methods to each class we need to serialize.

By means of these methods, it becomes straightforward to serialize even complex objects.

History

  • 5th February, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)