Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Custom Serialization in C#

Introduction

Serialization is the process of converting an object into a form that can be easily transported. In C#, this often means converting an object into a binary or XML format. Deserialization is the process of converting the serialized form back into an object. Custom serialization allows developers to control the serialization and deserialization processes, providing flexibility for handling special cases.

Why Custom Serialization?

Default serialization provided by .NET may not always meet specific requirements, such as:

  • Ignoring certain fields or properties
  • Customizing the format of serialized data
  • Encrypting data before serialization
  • Handling complex object graphs

Implementing Custom Serialization

In C#, custom serialization can be implemented by using the ISerializable interface. This interface requires the GetObjectData method to be implemented, which is responsible for populating a SerializationInfo object with the data needed to serialize the object.

Example: Custom Serialization

The following example demonstrates how to implement custom serialization for a class named Person.

using System;
using System.Runtime.Serialization;

[Serializable]
public class Person : ISerializable
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public Person() { }

    protected Person(SerializationInfo info, StreamingContext context)
    {
        FirstName = info.GetString("FirstName");
        LastName = info.GetString("LastName");
        Age = info.GetInt32("Age");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("FirstName", FirstName);
        info.AddValue("LastName", LastName);
        info.AddValue("Age", Age);
    }

    public override string ToString()
    {
        return $"{FirstName} {LastName}, Age: {Age}";
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person { FirstName = "John", LastName = "Doe", Age = 30 };

        // Serialize
        IFormatter formatter = new BinaryFormatter();
        using (Stream stream = new FileStream("person.bin", FileMode.Create, FileAccess.Write))
        {
            formatter.Serialize(stream, person);
        }

        // Deserialize
        using (Stream stream = new FileStream("person.bin", FileMode.Open, FileAccess.Read))
        {
            Person deserializedPerson = (Person)formatter.Deserialize(stream);
            Console.WriteLine(deserializedPerson);
        }
    }
}

Explanation

Let's break down the example:

  1. The Person class implements the ISerializable interface.
  2. The constructor Person(SerializationInfo info, StreamingContext context) is used during deserialization to reconstruct the object.
  3. The GetObjectData method populates the SerializationInfo object with the data needed for serialization.
  4. In the Main method, the Person object is serialized to a file named person.bin.
  5. The serialized file is then deserialized back into a Person object and printed to the console.

Advanced Custom Serialization

For more advanced scenarios, you may need to handle additional serialization logic, such as encrypting data or managing versioning. Here's an example that demonstrates encrypting a property before serialization:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;

[Serializable]
public class SecurePerson : ISerializable
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    private string EncryptedData { get; set; }

    public SecurePerson() { }

    protected SecurePerson(SerializationInfo info, StreamingContext context)
    {
        FirstName = info.GetString("FirstName");
        LastName = info.GetString("LastName");
        EncryptedData = info.GetString("EncryptedData");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("FirstName", FirstName);
        info.AddValue("LastName", LastName);

        // Encrypt the Age property
        string ageData = "Age=" + 30; // Example data to encrypt
        EncryptedData = Encrypt(ageData);
        info.AddValue("EncryptedData", EncryptedData);
    }

    private string Encrypt(string data)
    {
        byte[] dataBytes = Encoding.UTF8.GetBytes(data);
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes("A1234567890A1234567890A123456789");
            aes.IV = Encoding.UTF8.GetBytes("A1234567890A1234");
            using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
            {
                byte[] encryptedBytes = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length);
                return Convert.ToBase64String(encryptedBytes);
            }
        }
    }

    public override string ToString()
    {
        return $"{FirstName} {LastName}, EncryptedData: {EncryptedData}";
    }
}

class Program
{
    static void Main()
    {
        SecurePerson person = new SecurePerson { FirstName = "Jane", LastName = "Doe" };

        // Serialize
        IFormatter formatter = new BinaryFormatter();
        using (Stream stream = new FileStream("secureperson.bin", FileMode.Create, FileAccess.Write))
        {
            formatter.Serialize(stream, person);
        }

        // Deserialize
        using (Stream stream = new FileStream("secureperson.bin", FileMode.Open, FileAccess.Read))
        {
            SecurePerson deserializedPerson = (SecurePerson)formatter.Deserialize(stream);
            Console.WriteLine(deserializedPerson);
        }
    }
}

Conclusion

Custom serialization in C# provides a powerful mechanism to control how objects are serialized and deserialized. By implementing the ISerializable interface, you can handle special requirements such as ignoring fields, customizing data formats, encrypting data, and more. This tutorial covered the basics and provided examples to help you get started with custom serialization in C#.