BinaryFormatter deserialize gives SerializationException

前端 未结 6 948
别跟我提以往
别跟我提以往 2020-12-16 15:28

I\'m getting an:

System.Runtime.Serialization.SerializationException: Unable to find assembly \'myNameSpace, Version=1.0.0.0, Culture=neutral, Pub

相关标签:
6条回答
  • 2020-12-16 15:55

    If you don't have access to the original assembly that serialized the data, then you can use a SerializationBinder or a SerializationSurrogate. These two interfaces allow you to control how types are converted between one another when deserializing.

    0 讨论(0)
  • 2020-12-16 15:57

    You will need to provide a reference to the original type somehow so that the utility program knows how to deserialize it.

    The easy way is just to add the DLL the types were originally defined in as a reference to the utility project.

    The code you posted allows you to dynamically load that same DLL when the deserializer determines it can't find the type. This is a more difficult approach (but not that difficult), but in both cases you will need a DLL that defines the types... so probably easiest just to statically link by adding the reference.

    If your types are not currently in a DLL (e.g. if they are in an EXE), I suggest you pull the classes out of the EXE into a new DLL, and reference that DLL both from the original project and from the util project.

    0 讨论(0)
  • 2020-12-16 16:02

    You can get around this issue without needing the DLL if you know the object...

    http://spazzarama.com/2009/06/25/binary-deserialize-unable-to-find-assembly/

    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx

    Use the “System.Runtime.Serialization.SerializationBinder” class. By inheriting from this class it is possible to redirect all the requests for types from the binary formatter to the types of your choice.

    Here is a sample that will allow the types to be found in the current assembly regardless of which version of the assembly originally created the serialized stream:

    sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {     
            String currentAssembly = Assembly.GetExecutingAssembly().FullName;
    
            // In this case we are always using the current assembly
            assemblyName = currentAssembly;
    
            // Get the type using the typeName and assemblyName
            Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
                typeName, assemblyName));
    
            return typeToDeserialize;
        }
    }
    
    public static MyRequestObject Deserialize(byte[] b)
    {
        MyRequestObject mro = null;
        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        using (var ms = new System.IO.MemoryStream(b))
        {
           // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes)
           formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
    
           // Allow the exceptions to bubble up
           // System.ArgumentNullException
           // System.Runtime.Serialization.SerializationException
           // System.Security.SecurityException
           mro = (MyRequestObject)formatter.Deserialize(ms);
           ms.Close();
           return mro;
        }
    }
    
    0 讨论(0)
  • 2020-12-16 16:07

    I followed the solution answered by JTtheGeek and in order to get it work for me I had to add the following just before the statement assemblyName = currentAssembly;:

    typeName = "yourNamespace.yourClassName";
    

    After that it worked great!

    0 讨论(0)
  • 2020-12-16 16:14

    JTtheGeek's answer is correct that a custom SerializationBinder is the way to handle this problem. The example code given in that answer isn't sufficient for my use case, though. I needed something that could:

    1. Handle mismatched version numbers and namespaces.
    2. Look in all assemblies, not just the current one.
    3. Still be fast enough to call 100 times per second.

    This is what I came up with:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Company.Product.Common.Serialize
    {
        /// <summary>
        /// A Serialization Binder that allows inexact matches (version number or namespace).
        /// </summary>
        public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder
        {
            static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>();
    
            /// <summary>
            /// When overridden in a derived class, controls the binding of a serialized object to a type.
            /// </summary>
            /// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param>
            /// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param>
            /// <returns>
            /// The type of the object the formatter creates a new instance of.
            /// </returns>
            public override Type BindToType(string assemblyName, string typeName)
            {
                Type type;
                var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName);
    
                // use cached result if it exists
                if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type))
                {
                    return type;
                }
    
                // try the fully qualified name
                try { type = Type.GetType(assemblyQualifiedTypeName); }
                catch { type = null; }
    
                if (type == null)
                {
                    // allow any assembly version
                    var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(','));
                    var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion);
                    try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); }
                    catch { type = null; }
                }
    
                if (type == null)
                {
                    // check all assemblies for type full name
                    try
                    {
                        type = AppDomain.CurrentDomain.GetAssemblies()
                            .SelectMany(a => a.ExportedTypes)
                            .Where(a => a.FullName == typeName)
                            .FirstOrDefault();
                    }
                    catch { type = null; }
                }
    
                if (type == null)
                {
                    // check all assemblies for type name
                    var name = typeName.Split('.').Last();
                    try
                    {
                        type = AppDomain.CurrentDomain.GetAssemblies()
                            .SelectMany(a => a.ExportedTypes)
                            .Where(a => a.Name == name)
                            .FirstOrDefault();
                    }
                    catch { type = null; }
                }
    
                typeBindings[assemblyQualifiedTypeName] = type;
                return type;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-16 16:20

    I ran into a similar problem and I got it working with help of the following link: BinaryFormatterDeserialize-not-finding-a-type

    Basically what you need to do is subscribe to the AssemblyResolve event BEFORE deserializing. Then unsubscribe after deserialization..

    AppDomain.CurrentDomain.AssemblyResolve +=
                    new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    // CODE TO DESERIALIZE HERE
    
    AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    

    Here the method I used to resolve the Assembly:

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        try
        {
            if(args.Name == "MY ASSEMBLY NAME"))
            {
                //Load my Assembly 
                Assembly assem = Assembly.LoadFrom("MY ASSEMBLY PATH");
                if(assem != null)
                    return assem;
            }
        }
        catch { ;}
    
        return Assembly.GetExecutingAssembly();
    }
    
    0 讨论(0)
提交回复
热议问题