BinaryFormatter 反序列化给出 SerializationException

发布于 2024-08-19 01:36:43 字数 1046 浏览 4 评论 0原文

我得到一个:

System.Runtime.Serialization.SerializationException:无法找到 程序集'myNameSpace,版本= 1.0.0.0,文化=中性, PublicKeyToken=null

当尝试在另一个程序中反序列化某些数据而不是我序列化它的程序时。

经过一番谷歌搜索后,我发现显然这只能使用共享程序集来完成。

但是,我的数据库充满了这些序列化对象,我需要一个实用程序来将它们取出。有没有办法覆盖这种行为,只给它提供完全相同的类并强制它反序列化?


我已经找到了这个片段,但我不明白应该如何以及在哪里放置/使用它。

   static constructor() {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
        Assembly ayResult = null;
        string sShortAssemblyName = args.Name.Split(',')[0];
         Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
         foreach (Assembly ayAssembly in ayAssemblies) {
            if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
                 ayResult = ayAssembly;
                 break;
            }
         }
         return ayResult;
    }

I'm getting an:

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

When trying to deserialize some data in another program than the program I serialized it with.

After some googling I've found out that apparently this can only be done using a shared assembly.

However, my database is full with this serialized objects, and I need a utility program to get them out. Is there a way to override this behavior and just feed it the exact same class and force it do deserialize?


I already found this snippet, but I don't understand how and where I should put/use this.

   static constructor() {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
        Assembly ayResult = null;
        string sShortAssemblyName = args.Name.Split(',')[0];
         Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
         foreach (Assembly ayAssembly in ayAssemblies) {
            if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
                 ayResult = ayAssembly;
                 break;
            }
         }
         return ayResult;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

如果您知道该对象,则无需 DLL 即可解决此问题...

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

http://msdn.microsoft.com/en-us/library/system.runtime.serialization。序列化binder(VS.71).aspx

使用“System.Runtime.Serialization.SerializationBinder”类。经过
继承此类可以重定向所有请求
适用于从二进制格式化程序到您选择的类型的类型。

下面是一个示例,它将允许在当前程序集中找到类型,无论最初创建序列化流的程序集版本如何:

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;
    }
}

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;
    }
}
套路撩心 2024-08-26 01:36:43

您需要以某种方式提供对原始类型的引用,以便实用程序知道如何反序列化它。

最简单的方法就是添加最初定义类型的 DLL 作为对实用程序项目的引用。

您发布的代码允许您在解串器确定找不到类型时动态加载相同的 DLL。这是一种更困难的方法(但并不那么困难),但在这两种情况下,您都需要一个定义类型的 DLL...因此可能最简单的方法是通过添加引用来静态链接。

如果您的类型当前不在 DLL 中(例如,如果它们在 EXE 中),我建议您将这些类从 EXE 中提取到新的 DLL 中,并从原始项目和 util 项目引用该 DLL。

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.

撕心裂肺的伤痛 2024-08-26 01:36:43

我遇到了类似的问题,并在以下链接的帮助下解决了问题:
BinaryFormatter反序列化-not-finding-a-type

基本上,您需要做的是在反序列化之前订阅 AssemblyResolve 事件。然后反序列化后取消订阅。

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

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);

这里是我用来解析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();
}

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();
}
话少情深 2024-08-26 01:36:43

JTtheGeek 的答案是正确的,自定义 SerializationBinder 是处理此问题的方法。不过,该答案中给出的示例代码不足以满足我的用例。我需要的东西可以:

  1. 处理不匹配的版本号命名空间。
  2. 查看所有程序集,而不仅仅是当前程序集。
  3. 仍然足够快,每秒调用 100 次。

这就是我想出的:

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;
        }
    }
}

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;
        }
    }
}
苍白女子 2024-08-26 01:36:43

如果您无权访问序列化数据的原始程序集,则可以使用 SerializationBinder 或 SerializationSurrogate。这两个接口允许您控制反序列化时类型之间如何转换。

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.

っ左 2024-08-26 01:36:43

我遵循了 JTtheGeek 回答的解决方案,为了让它为我工作,我必须在语句 assemblyName = currentAssembly; 之前添加以下内容:

typeName = "yourNamespace.yourClassName";

之后效果很好!

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文