Dynamic Deserialization

Binary deserialization fails with a SerializationException saying ‘Unable to find assembly’ if you try to deserialize an object whose type is not referenced in the current application domain.

The easiest way to resolve this is to reference the assembly containing the required type. This way the BinaryFormatter will return an object of the same type and all you need to do is a simple cast:

var formatter = new BinaryFormatter();
var obj = (SourceType)formatter.Deserialize(stream);

However, this approach may not be possible for many reasons: You may not have access to the source assembly, the assembly may be targeting a different platform, etc. In such cases the solution is to create proxy types which have similar structure to the source type. Your BinaryFormatter then needs to be configured to deserialize the stream to a proxy type rather than the original one. This is done by creating a custom SerializationBinder:

sealed class MySerializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        return Type.GetType(String.Format("{0}, {1}", typeName, PROXY_ASSEMBLY_NAME);
    }
}

You then need to assign it to the formatter:

var formatter = new BinaryFormatter
{
    Binder = new MySerializationBinder ()
};

But there are still cases where you cannot even create proxy assemblies because the structure of the original types may be completely unknown to you. In such cases you should create a Descriptor Proxy which can represent a deserialized object in a dynamic way:

[Serializable]
class ObjectDescriptor : ISerializable
{
    private readonly SerializationInfoEnumerator enumerator;

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
    }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    private ObjectDescriptor(SerializationInfo info, StreamingContext context)
    {
        this.enumerator = info.GetEnumerator();
    }

    public IEnumerable<SerializationEntry> GetMembers()
    {
        this.enumerator.Reset();
        while (this.enumerator.MoveNext())
        {
            yield return this.enumerator.Current;
        }
    }
}

The descriptor object can then be used to create a dynamic object:

public dynamic CreateDynamicObject(ObjectDescriptor descriptor)
{
    var nameRegex = new Regex("<(.+)>");

    IDictionary<string, object> expando = new ExpandoObject();

    foreach (SerializationEntry member in descriptor.GetMembers())
    {
        string memberName = nameRegex.IsMatch(member.Name) ? nameRegex.Match(member.Name).Groups[1].Value : member.Name;
        expando[memberName] = member.Value is ObjectDescriptor complexMember ? CreateDynamicObject(complexMember) : member.Value;
    }

    return (ExpandoObject)expando;
}

Find a complete sample project implementing the above solution at this GitHub repository.

Advertisement