C_Sharp_Language

C Sharp TutorialC Sharp .NET Compiler Platform (Roslyn)C Sharp 3.0 FeaturesC Sharp 4.0 FeaturesC Sharp 5.0 FeaturesC Sharp 6.0 FeaturesC Sharp 7.0 FeaturesC Sharp Access ModifiersC Sharp Access network shared folder with username and passwordC Sharp Accessing DatabasesC Sharp Action FiltersC Sharp Aliases of built-in typesC Sharp an overview of collectionsC Sharp Anonymous typesC Sharp ArraysC Sharp ASP.NET IdentityC Sharp AssemblyInfo.cs ExamplesC Sharp Async-AwaitC Sharp Async/await, Backgroundworker, Task and Thread ExamplesC Sharp Asynchronous SocketC Sharp AttributesC Sharp Authentication handlerC Sharp BackgroundWorkerC Sharp BigIntegerC Sharp Binary SerializationC Sharp BindingListC Sharp Built-in TypesC Sharp CachingC Sharp CastingC Sharp Checked and UncheckedC Sharp CLSCompliantAttributeC Sharp Code ContractsC Sharp Code Contracts and AssertionsC Sharp Collection InitializersC Sharp Comments and regionsC Sharp Common String OperationsC Sharp Conditional StatementsC Sharp Constructors and FinalizersC Sharp Creating Own MessageBox in Windows Form ApplicationC Sharp Creational Design PatternsC Sharp Cryptography (System.Security.Cryptography)C Sharp Data AnnotationC Sharp DateTime MethodsC Sharp DelegatesC Sharp Dependency InjectionC Sharp DiagnosticsC Sharp Dynamic typeC Sharp EnumC Sharp Equality OperatorC Sharp Equals and GetHashCodeC Sharp EventsC Sharp Exception HandlingC Sharp Expression TreesC Sharp Extension MethodsC Sharp File and Stream I/OC Sharp FileSystemWatcherC Sharp Func delegatesC Sharp Function with multiple return valuesC Sharp Functional ProgrammingC Sharp Garbage Collector in .NETC Sharp Generating Random NumbersC Sharp Generic Lambda Query BuilderC Sharp GenericsC Sharp Getting Started: Json with C SharpC Sharp GuidC Sharp Handling FormatException when converting string to other typesC Sharp Hash FunctionsC Sharp ICloneableC Sharp IComparableC Sharp IDisposable interfaceC Sharp IEnumerableC Sharp ILGeneratorC Sharp ImmutabilityC Sharp Implementing Decorator Design PatternC Sharp Implementing Flyweight Design PatternC Sharp Import Google ContactsC Sharp Including Font ResourcesC Sharp IndexerC Sharp InheritanceC Sharp Initializing PropertiesC Sharp INotifyPropertyChanged interfaceC Sharp InterfacesC Sharp InteroperabilityC Sharp IQueryable interfaceC Sharp IteratorsC Sharp KeywordsC Sharp Lambda expressionsC Sharp Lambda ExpressionsC Sharp LINQ QueriesC Sharp LINQ to Objects



C Sharp Binary Serialization

From WikiOD

Remarks[edit | edit source]

The binary serialization engine is part of the .NET framework, but the examples given here are specific to C#. As compared to other serialization engines built into the .NET framework, the binary serializer is fast and efficient and usually requires very little extra code to get it to work. However, it is also less tolerant to code changes; that is, if you serialize an object and then make a slight change to the object's definition, it likely will not deserialize correctly.

Serialization Binder[edit | edit source]

The binder gives you an opportunity to inspect what types are being loaded in your application domain

Create a class inherited from SerializationBinder

class MyBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        if (typeName.Equals("BinarySerializationExample.Item"))
            return typeof(Item);
        return null;
    }
}

Now we can check what types are loading and on this basis to decide what we really want to receive

For using a binder, you must add it to the BinaryFormatter.

object DeserializeData(byte[] bytes)
{
    var binaryFormatter = new BinaryFormatter();
    binaryFormatter.Binder = new MyBinder();

    using (var memoryStream = new MemoryStream(bytes))
        return binaryFormatter.Deserialize(memoryStream);
}

The complete solution

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

namespace BinarySerializationExample
{
    class MyBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            if (typeName.Equals("BinarySerializationExample.Item"))
                return typeof(Item);
            return null;
        }
    }

    [Serializable]
    public class Item
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var item = new Item
            {
                Name = "Orange"
            };

            var bytes = SerializeData(item);    
            var deserializedData = (Item)DeserializeData(bytes);
        }

        private static byte[] SerializeData(object obj)
        {
            var binaryFormatter = new BinaryFormatter();
            using (var memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, obj);
                return memoryStream.ToArray();
            }
        }

        private static object DeserializeData(byte[] bytes)
        {
            var binaryFormatter = new BinaryFormatter
            {
                Binder = new MyBinder()
            };

            using (var memoryStream = new MemoryStream(bytes))
                return binaryFormatter.Deserialize(memoryStream);
        }
    }
}

Controlling serialization behavior with attributes[edit | edit source]

If you use the [NonSerialized] attribute, then that member will always have its default value after deserialization (ex. 0 for an int, null for string, false for a bool, etc.), regardless of any initialization done in the object itself (constructors, declarations, etc.). To compensate, the attributes [OnDeserializing] (called just BEFORE deserializing) and [OnDeserialized] (called just AFTER deserializing) together with their counterparts, [OnSerializing] and [OnSerialized] are provided.

Assume we want to add a "Rating" to our Vector and we want to make sure the value always starts at 1. The way it is written below, it will be 0 after being deserialized:

[Serializable]
public class Vector
{
    public int X;
    public int Y;
    public int Z;

    [NonSerialized]
    public decimal Rating = 1M;

    public Vector()
    {
        Rating = 1M;
    }

    public Vector(decimal initialRating)
    {
        Rating = initialRating;
    }
}

To fix this problem, we can simply add the following method inside of the class to set it to 1:

[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
    Rating = 1M;
}

Or, if we want to set it to a calculated value, we can wait for it to be finished deserializing and then set it:

[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
    Rating = 1 + ((X+Y+Z)/3);
}

Similarly, we can control how things are written out by using [OnSerializing] and [OnSerialized].

Some gotchas in backward compatibility[edit | edit source]

This small example shows how you can lose backward compatibility in your programs if you do not take care in advance about this. And ways to get more control of serialization process

At first, we will write an example of the first version of the program:

Version 1

[Serializable]
class Data
{
    [OptionalField]
    private int _version;

    public int Version
    {
        get { return _version; }
        set { _version = value; }
    }
}

And now, let us assume that in the second version of the program added a new class. And we need to store it in an array.

Now code will look like this:

Version 2

[Serializable]
class NewItem
{
    [OptionalField]
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

[Serializable]
class Data
{
    [OptionalField]
    private int _version;

    public int Version
    {
        get { return _version; }
        set { _version = value; }
    }

    [OptionalField]
    private List<NewItem> _newItems;

    public List<NewItem> NewItems
    {
        get { return _newItems; }
        set { _newItems = value; }
    }
}

And code for serialize and deserialize

private static byte[] SerializeData(object obj)
{
    var binaryFormatter = new BinaryFormatter();
    using (var memoryStream = new MemoryStream())
    {
        binaryFormatter.Serialize(memoryStream, obj);
        return memoryStream.ToArray();
    }
}

private static object DeserializeData(byte[] bytes)
{
    var binaryFormatter = new BinaryFormatter();
    using (var memoryStream = new MemoryStream(bytes))
        return binaryFormatter.Deserialize(memoryStream);
}

And so, what would happen when you serialize the data in the program of v2 and will try to deserialize them in the program of v1?

You get an exception:

System.Runtime.Serialization.SerializationException was unhandled
Message=The ObjectManager found an invalid number of fixups. This usually indicates a problem in the Formatter.Source=mscorlib
StackTrace:
   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 Microsoft.Samples.TestV1.Main(String[] args) in c:\Users\andrew\Documents\Visual Studio 2013\Projects\vts\CS\V1 Application\TestV1Part2\TestV1Part2.cs:line 29
   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Why?

The ObjectManager has a different logic to resolve dependencies for arrays and for reference and value types. We added an array of new the reference type which is absent in our assembly.

When ObjectManager attempts to resolve dependencies it builds the graph. When it sees the array, it can not fix it immediately, so that it creates a dummy reference and then fixes the array later.

And since this type is not in the assembly and dependencies can’t be fixed. For some reason, it does not remove the array from the list of elements for the fixes and at the end, it throws an exception “IncorrectNumberOfFixups”.

It is some ‘gotchas’ in the process of serialization. For some reason, it does not work correctly only for arrays of new reference types.

A Note:
Similar code will work correctly if you do not use arrays with new classes

And the first way to fix it and maintain compatibility?

  • Use a collection of new structures rather than classes or use a dictionary(possible classes), because a dictionary it’s a collection of keyvaluepair(it’s structure)
  • Use ISerializable, if you can't change the old code

Making an object serializable[edit | edit source]

Add the [Serializable] attribute to mark an entire object for binary serialization:

[Serializable]
public class Vector
{
    public int X;
    public int Y;
    public int Z;

    [NonSerialized]
    public decimal DontSerializeThis;

    [OptionalField]
    public string Name;
}

All members will be serialized unless we explicitly opt-out using the [NonSerialized] attribute. In our example, X, Y, Z, and Name are all serialized.

All members are required to be present on deserialization unless marked with [NonSerialized] or [OptionalField]. In our example, X, Y, and Z are all required and deserialization will fail if they are not present in the stream. DontSerializeThis will always be set to default(decimal) (which is 0). If Name is present in the stream, then it will be set to that value, otherwise it will be set to default(string) (which is null). The purpose of [OptionalField] is to provide a bit of version tolerance.

Serialization surrogates (Implementing ISerializationSurrogate)[edit | edit source]

Implements a serialization surrogate selector that allows one object to perform serialization and deserialization of another

As well allows to properly serialize or deserialize a class that is not itself serializable

Implement ISerializationSurrogate interface

public class ItemSurrogate : ISerializationSurrogate
{
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var item = (Item)obj;
        info.AddValue("_name", item.Name);
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var item = (Item)obj;
        item.Name = (string)info.GetValue("_name", typeof(string));
        return item;
    }
}

Then you need to let your IFormatter know about the surrogates by defining and initializing a SurrogateSelector and assigning it to your IFormatter

var surrogateSelector = new SurrogateSelector();
surrogateSelector.AddSurrogate(typeof(Item), new StreamingContext(StreamingContextStates.All), new ItemSurrogate());    
var binaryFormatter = new BinaryFormatter
{
    SurrogateSelector = surrogateSelector
};

Even if the class is not marked serializable.

//this class is not serializable
public class Item
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

The complete solution

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

namespace BinarySerializationExample
{
    class Item
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }

    class ItemSurrogate : ISerializationSurrogate
    {
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            var item = (Item)obj;
            info.AddValue("_name", item.Name);
        }

        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            var item = (Item)obj;
            item.Name = (string)info.GetValue("_name", typeof(string));
            return item;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var item = new Item
            {
                Name = "Orange"
            };

            var bytes = SerializeData(item);
            var deserializedData = (Item)DeserializeData(bytes);
        }

        private static byte[] SerializeData(object obj)
        {
            var surrogateSelector = new SurrogateSelector();
            surrogateSelector.AddSurrogate(typeof(Item), new StreamingContext(StreamingContextStates.All), new ItemSurrogate());

            var binaryFormatter = new BinaryFormatter
            {
                SurrogateSelector = surrogateSelector
            };

            using (var memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, obj);
                return memoryStream.ToArray();
            }
        }

        private static object DeserializeData(byte[] bytes)
        {
            var surrogateSelector = new SurrogateSelector();
            surrogateSelector.AddSurrogate(typeof(Item), new StreamingContext(StreamingContextStates.All), new ItemSurrogate());

            var binaryFormatter = new BinaryFormatter
            {
                SurrogateSelector = surrogateSelector
            };

            using (var memoryStream = new MemoryStream(bytes))
                return binaryFormatter.Deserialize(memoryStream);
        }
    }
}

Adding more control by implementing ISerializable[edit | edit source]

That would get more control over serialization, how to save and load types

Implement ISerializable interface and create an empty constructor to compile

[Serializable]
public class Item : ISerializable
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Item ()
    {

    }

    protected Item (SerializationInfo info, StreamingContext context)
    {
        _name = (string)info.GetValue("_name", typeof(string));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("_name", _name, typeof(string));
    }
}

For data serialization, you can specify the desired name and the desired type

info.AddValue("_name", _name, typeof(string));

When the data is deserialized, you will be able to read the desired type

_name = (string)info.GetValue("_name", typeof(string));

Credit:Stack_Overflow_Documentation