Custom Serialization in C#

What is Custom Serialization in C#?

**Custom Serialization** allows developers to **control how an object is serialized and deserialized** instead of relying on the default behavior. This is useful when working with **private fields, complex objects, or version compatibility**.

Key Features of Custom Serialization

  • Gives **full control** over the serialization process.
  • Allows **excluding specific fields** dynamically.
  • Useful for **handling private fields and encrypting data**.
  • Supports **Binary, XML, and JSON serialization**.

Implementing Custom Serialization Using ISerializable

The **ISerializable** interface allows defining **custom serialization logic**.

Example: Custom Serialization Using ISerializable

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

[Serializable]
class User : ISerializable
{
    public string Username { get; set; }
    private string Password { get; set; }

    public User() { }

    public User(string username, string password)
    {
        Username = username;
        Password = password;
    }

    // Custom Serialization
    protected User(SerializationInfo info, StreamingContext context)
    {
        Username = info.GetString("Username");
        Password = Decrypt(info.GetString("Password"));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Username", Username);
        info.AddValue("Password", Encrypt(Password));
    }

    private string Encrypt(string data) => $"encrypted({data})";
    private string Decrypt(string data) => data.Replace("encrypted(", "").Replace(")", "");

    public string GetPassword() => Password;
}

// Writing Custom Serialized Object
class Program
{
    static void Main()
    {
        User user = new User("Alice", "SuperSecret123");
        BinaryFormatter formatter = new BinaryFormatter();

        using (FileStream stream = new FileStream("user.dat", FileMode.Create))
        {
            formatter.Serialize(stream, user);
        }

        Console.WriteLine("User object serialized with encryption.");
    }
}

// Output:
// User object serialized with encryption.
        

The **ISerializable** interface allows **encrypting passwords** during serialization.

Custom JSON Serialization

The **System.Text.Json** library allows defining custom serialization using **JsonConverter**.

Example: Custom JSON Serialization Using JsonConverter

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

class User
{
    public string Username { get; set; }

    [JsonConverter(typeof(PasswordConverter))]
    public string Password { get; set; }
}

// Custom Password Converter
class PasswordConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return $"decrypted({reader.GetString()})";
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        writer.WriteStringValue($"encrypted({value})");
    }
}

// Writing Custom JSON Data
class Program
{
    static void Main()
    {
        User user = new User { Username = "Bob", Password = "MySecret123" };
        string json = JsonSerializer.Serialize(user);

        Console.WriteLine("Serialized JSON:");
        Console.WriteLine(json);
    }
}

// Output:
// Serialized JSON:
// {"Username":"Bob","Password":"encrypted(MySecret123)"} 
        

The **JsonConverter** class allows **encrypting and decrypting JSON fields**.

Custom XML Serialization

The **IXmlSerializable** interface allows defining **custom XML serialization** logic.

Example: Custom XML Serialization Using IXmlSerializable

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

public class User : IXmlSerializable
{
    public string Username { get; set; }
    private string Password { get; set; }

    public User() { }
    public User(string username, string password)
    {
        Username = username;
        Password = password;
    }

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        reader.MoveToContent();
        Username = reader.GetAttribute("Username");
        Password = Decrypt(reader.GetAttribute("Password"));
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Username", Username);
        writer.WriteAttributeString("Password", Encrypt(Password));
    }

    private string Encrypt(string data) => $"encrypted({data})";
    private string Decrypt(string data) => data.Replace("encrypted(", "").Replace(")", "");
}

// Writing XML Data
class Program
{
    static void Main()
    {
        User user = new User("Charlie", "Secret789");
        XmlSerializer serializer = new XmlSerializer(typeof(User));

        using (FileStream stream = new FileStream("user.xml", FileMode.Create))
        {
            serializer.Serialize(stream, user);
        }

        Console.WriteLine("User object serialized to XML.");
    }
}

// Output:
// User object serialized to XML.
        

The **IXmlSerializable** interface allows **encrypting fields in XML serialization**.

Best Practices for Custom Serialization

  • Use **ISerializable** for **BinaryFormatter** when private fields need serialization.
  • For **JSON serialization**, use **JsonConverter** to **modify data** before storage.
  • Use **IXmlSerializable** for **custom XML serialization logic**.
  • Ensure **encryption or hashing** when dealing with **sensitive data**.