XmlSerializer在构造函数中给出FileNotFoundException

时间:2009-07-14 19:19:25

标签: c# xml-serialization

当我尝试序列化类型时,我一直在使用的应用程序失败。

这样的陈述
XmlSerializer lizer = new XmlSerializer(typeof(MyType));

产生

System.IO.FileNotFoundException occurred
  Message="Could not load file or assembly '[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
  Source="mscorlib"
  FileName="[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  FusionLog=""
  StackTrace:
       at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)

我没有为我的班级定义任何特殊的序列化器。

如何解决此问题?

19 个答案:

答案 0 :(得分:364)

信不信由你,这是正常行为。抛出异常但由XmlSerializer处理,所以如果你忽略它,一切都应该继续正常。

我发现这很烦人,如果你搜索一下就会有很多关于这一点的抱怨,但是从我读过的内容来看,微软并不打算做任何事情。

如果关闭该特定异常的第一次机会异常,则可以避免在调试时始终获取异常弹出窗口。在Visual Studio中,转到 Debug - > 例外(或按 Ctrl + Alt + E ),公共语言运行时例外 - > System.IO - > System.IO.FileNotFoundException

您可以在博客文章 C# XmlSerializer FileNotFound exception (Chris Sells的工具 XmlSerializerPreCompiler )中找到有关其他方式的信息。

答案 1 :(得分:100)

像马丁·谢尔本所说,这是正常行为。 XmlSerializer的构造函数首先尝试查找名为[YourAssembly] .XmlSerializers.dll的程序集,该程序集应包含用于序列化类型的生成类。由于尚未生成此类DLL(默认情况下不是这样),因此抛出FileNotFoundException。当发生这种情况时,XmlSerializer的构造函数捕获该异常,并且XmlSerializer的构造函数在运行时自动生成DLL(这是通过在计算机的%temp%目录中生成C#源文件,然后使用C#编译器编译它们来完成的)。对于相同类型的XmlSerializer的其他构造将仅使用已经生成的DLL。

  

更新:从.NET 4.5开始,XmlSerializer不再执行代码生成,也不会使用C#编译器执行编译,以便在运行时创建序列化程序程序集,除非明确强制通过设置配置文件设置(useLegacySerializerGeneration)。此更改消除了对csc.exe的依赖性并提高了启动性能。 来源:.NET Framework 4.5 Readme,第1.3.8.1节。

异常由XmlSerializer的构造函数处理。您无需自己做任何事情,只需单击“继续”(F5)继续执行您的程序,一切都会好的。如果您对停止执行程序并弹出异常帮助程序的异常感到困扰,则可以关闭“Just My Code”,或者将FileNotFoundException设置为在抛出时执行,而不是在“User-未处理”。

要启用“仅我的代码”,请转到工具>>选项>>调试>>一般>>启用我的代码。要在抛出FileNotFound时关闭执行中断,请转到Debug>>例外>>查找>>输入'FileNotFoundException'>>从System.IO.FileNotFoundException中取消“Thrown”复选框。

答案 2 :(得分:63)

在Visual Studio项目属性(“构建”页面,如果我没记错),有一个选项说“生成序列化程序集”。尝试为生成 [包含MyType程序集] 的项目打开它。

答案 3 :(得分:58)

有一种解决方法。如果你使用

XmlSerializer lizer = XmlSerializer.FromTypes(new[] { typeof(MyType) })[0];

它应该避免这种例外。这对我有用。

警告: 请勿多次使用,否则您将遇到内存泄漏

如果您使用此方法多次为同一类型创建XmlSerializer的实例,则会像疯了一样泄漏内存!

这是因为此方法绕过了XmlSerializer(type)XmlSerializer(type, defaultNameSpace)构造函数(所有其他构造函数也绕过缓存)提供的内置缓存。

如果你使用任何方法创建一个不通过这两个构造函数的XmlSerializer,你必须实现自己的缓存,否则你会出血。

答案 4 :(得分:21)

我遇到了这个问题,无法通过提到的任何解决方案解决这个问题。

然后我终于找到了解决方案。 似乎序列化程序不仅需要类型,还需要嵌套类型。 改变这个:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

对此:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T).GetNestedTypes());

为我解决了这个问题。 没有更多的例外或任何事情。

答案 5 :(得分:9)

我的解决方案是直接进行反射以创建序列化程序。这绕过了导致异常的奇怪文件加载。我将它打包在一个辅助函数中,该函数也负责缓存序列化程序。

private static readonly Dictionary<Type,XmlSerializer> _xmlSerializerCache = new Dictionary<Type, XmlSerializer>();

public static XmlSerializer CreateDefaultXmlSerializer(Type type) 
{
    XmlSerializer serializer;
    if (_xmlSerializerCache.TryGetValue(type, out serializer))
    {
        return serializer;
    }
    else
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(type, null, null);
        serializer = new XmlSerializer(mapping);
        return _xmlSerializerCache[type] = serializer;
    }
}

答案 6 :(得分:7)

为避免异常,您需要做两件事:

  1. 向序列化类添加属性(我希望您有权访问)
  2. 使用sgen.exe生成序列化文件
  3. 将System.Xml.Serialization.XmlSerializerAssembly属性添加到您的班级。 将'MyAssembly'替换为MyClass所在的程序集的名称。

    [Serializable]
    [XmlSerializerAssembly("MyAssembly.XmlSerializers")]
    public class MyClass
    {
    …
    }
    

    使用sgen.exe实用程序生成序列化文件,并使用类的程序集部署它。

    'sgen.exe MyAssembly.dll'将生成文件MyAssembly.XmlSerializers.dll

    这两个更改将导致.net直接查找程序集。 我检查了它,它适用于使用Visual Studio 2008的.NET framework 3.5

答案 7 :(得分:6)

此异常也可以被称为BindingFailure的managed debugging assistant(MDA)捕获。

如果您的应用程序旨在提供预构建的序列化程序集,则此MDA非常有用。我们这样做是为了提高应用程序的性能。它允许我们确保构建过程正确构建预构建的序列化程序集,并由应用程序加载而无需动态重新构建。

除了在这种情况下,它确实没用,因为正如其他海报所说,当Serializer构造函数捕获绑定错误时,序列化程序集在运行时重新构建。所以你通常可以把它关掉。

答案 8 :(得分:6)

函数XmlSerializer.FromTypes不会抛出异常,但会泄漏内存。这就是为什么你需要为每种类型缓存这样的序列化器,以避免每个创建的实例的内存泄漏。

创建您自己的XmlSerializer工厂并简单地使用它:

XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));

工厂看起来像:

public static class XmlSerializerFactoryNoThrow
{
    public static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();

    private static object SyncRootCache = new object();        

    /// <summary>
    /// //the constructor XmlSerializer.FromTypes does not throw exception, but it is said that it causes memory leaks
    /// http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor
    /// That is why I use dictionary to cache the serializers my self.
    /// </summary>
    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            //constructor XmlSerializer.FromTypes does not throw the first chance exception           
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            //serializer = XmlSerializerFactoryNoThrow.Create(type);
        }

        lock (SyncRootCache)
        {
            _cache[type] = serializer;
        }
        return serializer;
    }       
}

更复杂的版本没有内存泄漏的可能性(请有人查看代码):

    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            lock (SyncRootCache)
            {
                if (_cache.TryGetValue(type, out serializer))
                    return serializer;
            }
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            lock (SyncRootCache)
            {
                _cache[type] = serializer;
            }
        }          
        return serializer;
    }       
}

答案 9 :(得分:3)

另一方面,编译错误的排除非常复杂。这些问题在FileNotFoundException中表现为消息:

File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
   at System.Reflection.Assembly.nLoad( ... )
   at System.Reflection.Assembly.InternalLoad( ... )
   at System.Reflection.Assembly.Load(...)
   at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() 

您可能想知道未找到文件异常与实例化序列化程序对象有什么关系,但请记住:构造函数编写C#文件并尝试编译它们。此异常的调用堆栈提供了一些支持该怀疑的良好信息。 XmlSerializer尝试加载由CodeDOM生成的程序集,调用System.Reflection.Assembly.Load方法时发生异常。该例外没有解释为什么XmlSerializer应该创建的程序集不存在。通常,程序集不存在,因为编译失败,这可能发生,因为在极少数情况下,序列化属性会产生C#编译器无法编译的代码。

注意 当XmlSerializer在无法访问临时目录的帐户或安全环境下运行时,也会发生此错误。

<强>来源http://msdn.microsoft.com/en-us/library/aa302290.aspx

答案 10 :(得分:2)

在Visual Studio项目属性中,有一个选项说“生成序列化程序集”。尝试为生成[包含MyType程序集]的项目启用它。

答案 11 :(得分:1)

要序列化的自定义类:

[Serializable]
public class TestClass
{
    int x = 2;
    int y = 4;
    public TestClass(){}
    public TestClass(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int TestFunction()
    {
        return x + y;
    }
}

我附上了代码段。也许这可以帮到你。

static void Main(string[] args)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(TestClass));

    MemoryStream memoryStream = new MemoryStream();
    XmlTextWriter xmlWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);

    TestClass domain = new TestClass(10, 3);
    xmlSerializer.Serialize(xmlWriter, domain);
    memoryStream = (MemoryStream)xmlWriter.BaseStream;
    string xmlSerializedString = ConvertByteArray2Str(memoryStream.ToArray());

    TestClass xmlDomain = (TestClass)DeserializeObject(xmlSerializedString);

    Console.WriteLine(xmlDomain.TestFunction().ToString());
    Console.ReadLine();
}

答案 12 :(得分:1)

仅供参考。从D-B回答和评论,我得到了这个接近D-B解决方案的解决方案。它适用于我的所有情况,它是线程安全的。我不认为使用ConcurrentDictionary会没问题。

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace HQ.Util.General
{
    public class XmlSerializerHelper
    {
        private static readonly Dictionary<Type, XmlSerializer> _dictTypeToSerializer = new Dictionary<Type, XmlSerializer>();

        public static XmlSerializer GetSerializer(Type type)
        {
            lock (_dictTypeToSerializer)
            {
                XmlSerializer serializer;
                if (! _dictTypeToSerializer.TryGetValue(type, out serializer))
                {
                    var importer = new XmlReflectionImporter();
                    var mapping = importer.ImportTypeMapping(type, null, null);
                    serializer = new XmlSerializer(mapping);
                    return _dictTypeToSerializer[type] = serializer;
                }

                return serializer;
            }
        }
    }
}

用法:

        if (File.Exists(Path))
        {
            using (XmlTextReader reader = new XmlTextReader(Path))
            {
                // XmlSerializer x  = new XmlSerializer(typeof(T));
                var x = XmlSerializerHelper.GetSerializer(typeof(T));

                try
                {
                    options = (OptionsBase<T>)x.Deserialize(reader);
                }
                catch (Exception ex)
                {
                    Log.Instance.AddEntry(LogType.LogException, "Unable to open Options file: " + Path, ex);
                }
            }
        }

答案 13 :(得分:1)

我遇到了类似的问题,忽略异常并不适用于我。我的代码调用了NServiceBus的配置Configure.With(...).XmlSerializer()...

为我修复的是改变我项目的平台。

  1. 转到Build \ Configuration Manager ...
  2. 找到你的项目并更改平台(在我的情况下从x86到任何CPU)

答案 14 :(得分:0)

我的一个.Net Standard dll中存在类似的问题。

我使用了Microsoft.XmlSerializer.Generator nuget,它可以在.Net Core和.Net Standard上预生成XmlSerializer。

答案 15 :(得分:0)

看到了很多使用ConcurrentDictionary的建议,但没有可靠的例子,因此我将全力以赴参加解决方案竞赛。我不是线程安全的开发人员,因此,如果此代码不够可靠,请为那些关注此事的人大声疾呼。

public static class XmlSerializerHelper
{
    private static readonly ConcurrentDictionary<Type, XmlSerializer> TypeSerializers = new ConcurrentDictionary<Type, XmlSerializer>();

    public static XmlSerializer GetSerializer(Type type)
    {
        return TypeSerializers.GetOrAdd(type,
        t =>
        {
            var importer = new XmlReflectionImporter();
            var mapping = importer.ImportTypeMapping(t, null, null);
            return new XmlSerializer(mapping);
        });
    }
}

我看过其他涉及ConcurrentDictionaryLazy的帖子正在加载该值。我不确定这是否有意义,但这是该代码:

private static readonly ConcurrentDictionary<Type, Lazy<XmlSerializer>> TypeSerializers = new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();

public static XmlSerializer GetSerializer(Type type)
{
    return TypeSerializers.GetOrAdd(type,
    t =>
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(t, null, null);
        var lazyResult = new Lazy<XmlSerializer>(() => new XmlSerializer(mapping), LazyThreadSafetyMode.ExecutionAndPublication);
        return lazyResult;
    }).Value;
}

答案 16 :(得分:0)

我遇到了同样的问题,直到我使用第三方工具从XSD生成类并且它工作了!我发现该工具在我的课程顶部添加了一些额外的代码。当我将相同的代码添加到我的原始类的顶部时,它工作。这是我添加的......

#pragma warning disable
namespace MyNamespace
{
  using System;
  using System.Diagnostics;
  using System.Xml.Serialization;
  using System.Collections;
  using System.Xml.Schema;
  using System.ComponentModel;
  using System.Xml;
  using System.Collections.Generic;

  [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1064.2")]
  [System.SerializableAttribute()]
  [System.Diagnostics.DebuggerStepThroughAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
  [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
  public partial class MyClassName
  {
  ...

答案 17 :(得分:0)

我得到了同样的错误,这是由于我试图反序列化没有默认无参数构造函数的类型。我添加了一个构造函数,它开始工作了。

答案 18 :(得分:0)

您的类型可能会引用在GAC或本地bin文件夹中找不到的其他程序集==&gt; ......

  

“或其中一个依赖项。系统   找不到指定的文件“

您能举例说明要序列化的类型吗?

注意:确保您的类型实现Serializable。