Click here to Skip to main content
14,921,476 members
Articles / Programming Languages / C#
Tip/Trick
Posted 18 May 2016

Stats

24.6K views
751 downloads
8 bookmarked

How to Serialize Across Assemblies with the BinaryFormatter

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
18 May 2016CPOL2 min read
Describes how to write your own SerializationBinder to allow serialization across assemblies

Introduction

I have fielded several questions now on how to serialize an object from one assembly and deserialize it into an equivalent object in another assembly.

Normally, you would use JSON or XML to achieve this but if you want/need to use the BinaryFormatter to create a much smaller, faster more efficient serialization, deserializing your object in a different assembly can be tricky.

The way to achieve this is to implement your own SerializationBinder. This is the object that is responsible for finding types during serialization and deserialization.

To implement your own binder, you derive a new class from the System.Runtime.Serialization.SerializationBinder class, and override the BindToType and BindToName methods:

C#
/// <summary>
/// look up the type locally if the assembly-name is "NA"
/// </summary>
/// <param name="assemblyName"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public override Type BindToType(string assemblyName, string typeName)
{
    if (assemblyName.Equals("NA"))
        return Type.GetType(typeName);
    else
        return defaultBinder.BindToType(assemblyName, typeName);
}

/// <summary>
/// override BindToName in order to strip the assembly name. Setting assembly name to null does nothing.
/// </summary>
/// <param name="serializedType"></param>
/// <param name="assemblyName"></param>
/// <param name="typeName"></param>
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
    // specify a neutral code for the assembly name to be recognized by the BindToType method.
    assemblyName = "NA";
    typeName     = serializedType.FullName;
}

The BindToName method is where the assembly-name and type-name are chosen to be written into the serialization. So your type can be de-serialized in another assembly, we are setting the assembly-name to "NA" - if you leave the assembly-name as NULL, the normal assembly name will be written into the stream, which is why we set a non-null value (you could use a zero-length string)

The BindToType method is essentially the reverse of the BindToName method. This is the method responsible for finding the correct type to de-serialize into.

Here, I am testing if the assembly-name is "NA", then performing a local search for the type-name using Type.GetType(string typeName).

To use the serialization binder, you simply set the Binder property of a new BinaryFormatter to an instance of your SerializationBinder. In the example, I have used a singleton for efficiency.

C#
public static void Serialize<T>(this T graph, Stream target)
{
    // create the formatter:
    IFormatter formatter = new BinaryFormatter();
    
    // set the binder to the custom binder:
    formatter.Binder = TypeOnlyBinder.Default;
    
    // serialize the object into the stream:
    formatter.Serialize(target, graph);
}

public static T DeSerialize<T>(this Stream source)
{
    // create the formatter:
    IFormatter formatter = new BinaryFormatter();
    
    // set the binder to the custom binder:
    formatter.Binder = TypeOnlyBinder.Default;
    
    // serialize the object into the stream:
    return (T)formatter.Deserialize(source);
}

Using the Code

In the attached example solution, there are two console projects. Both define an object called "SimpleObject" in a namespace called "Serialization" - the two objects are identical, but are defined in separate assemblies.

C#
[Serializable]
public class SimpleObject
{
    public string   Property { get; set; }
    public DateTime Date     { get; set; } = DateTime.Now;
}

One project creates a new instance of a SimpleObject, and serializes it into a file called "example.bin" in the solution directory:

C#
static void Main(string[] args)
{
    // example code:
    var so = new SimpleObject { Property = "Test Property Value" };

    Console.WriteLine($"Simple Object Type: {so.GetType().AssemblyQualifiedName}");

    // serialize: go up sufficient directories that the file lands in the solution dir.
    using (var fs = File.OpenWrite("..\\..\\..\\example.bin"))
    {
        so.Serialize(fs);
    }
}

The other project "DeSerializeOtherAssembly" loads the example.bin file and deserializes it into that assembly's version of "SimpleObject":

C#
static void Main(string[] args)
{
    using (var fs = File.OpenRead("..\\..\\..\\example.bin"))
    {
        var so = fs.DeSerialize<SimpleObject>();

        Console.WriteLine($"SimpleObject Type: {so.GetType().AssemblyQualifiedName}");
        Console.WriteLine($"SimpleObject.Property = {so.Property}");
        Console.WriteLine($"SimpleObject.Date = {so.Date}");
    }
}

NOTE

The code is written in C# 6.0, you might have a couple of syntax errors in any version of Visual Studio prior to 2015.

Points of Interest

The Serializer class used in the example is a pretty good helper object for binary serialization - it uses extension methods with generics for quick and easy strongly-typed serialization and deserialization.

History

  • @ Version 1.0

License

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

Share

About the Author

Simon Bridge
Software Developer (Senior) Decipha
Australia Australia
Wrote his first computer game in Microsoft Basic, on a Dragon 32 at age 7. It wasn't very good.
Has been working as a consultant and developer for the last 15 years,
Discovered C# shortly after it was created, and hasn't looked back.
Feels weird talking about himself in the third person.

Comments and Discussions

 
QuestionHaving error "Unable to find assembly 'NA' " when deSerializing SimpleObject from MemoryMappedViewStream instead of file stream Pin
Member 328711329-Apr-20 16:23
MemberMember 328711329-Apr-20 16:23 
Question.NET 3.5 Pin
Member 1398479715-Sep-18 1:26
MemberMember 1398479715-Sep-18 1:26 
SuggestionUse a converter to your new assembly Pin
Rene Balvert19-May-16 3:25
MemberRene Balvert19-May-16 3:25 
GeneralRe: Use a converter to your new assembly Pin
Simon Bridge19-May-16 13:37
MemberSimon Bridge19-May-16 13:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.