XmlSerializer在构造函数中给出FileNotFoundException

Irw*_*win 340 c# xml-serialization

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

像这样的陈述

XmlSerializer lizer = new XmlSerializer(typeof(MyType));
Run Code Online (Sandbox Code Playgroud)

生产:

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)
Run Code Online (Sandbox Code Playgroud)

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

我该如何解决这个问题?

Mar*_*urn 375

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

我发现这很烦人,已经有很多抱怨这一点,如果你周围有点搜索,但是从我读过微软不准备做任何事情的计划.

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

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

  • 解决此问题的一种可能方法是在工具 - >选项 - >调试 - >常规选项中选中"仅我的代码"选项. (159认同)
  • 我认为汉斯的建议更有价值 - 使用不会产生此异常的不同方法调用:XmlSerializer serializer = XmlSerializer.FromTypes(new [] {typeof(MyType)})[0]; (27认同)
  • @Frederic:这个评论太棒了!我坐在这里有一个"WTF!?" 表达在我的脸上,试图捕捉这个虚假的异常,我发现这个问题,回答(这是微软的错,还有什么是新的?),但我不想禁用异常处理,因为我可能需要它我的代码.A +! (25认同)
  • 对不起,这是一个可怕的建议.根据我的经验,FileNotFoundException是更常见的一种,并且禁用此异常报告只是在将来某天会遇到麻烦.最好打开"Just My Code"或启用下面描述的序列化程序集的创建. (15认同)
  • 问题是我的测试失败了,所以我不能只是"忽略"异常 (3认同)
  • @DRAirey1:当然,这是最常见的之一;但也是可以合理预期触发并预期处理的代码之一。.NET 中没有办法避免与 IO 相关的异常。只要捕获到异常,就没有什么关系。 (2认同)

All*_*nek 104

就像Martin Sherburn所说,这是正常的行为.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自述文件,第1.3.8.1节.

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

要启用"Just My Code",请转到工具>>选项>>调试>>常规>>启用我的代码.要在抛出FileNotFound时关闭执行中断,请转到Debug >> Exceptions >> Find >> enter'FileNotFoundException'>>取消选中System.IO.FileNotFoundException中的'Thrown'复选框.

  • 您的更新表明在.NET 4.5中不应发生此异常,但我仍然看到它. (3认同)
  • 我只是希望 MS 能够像 (File.Exists(...)) { Load } else { Fallback } 一样实现这一点,而不是 try { Load } catch { Fallback } 。基于异常的流量控制听起来很糟糕,让我的调试体验变得比必要的更加困难和脆弱。 (2认同)

Vla*_*adV 63

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

  • 如果序列化程序集仍未由Visual Studio生成,请参阅http://stackoverflow.com/a/8798289/1164966. (4认同)

qua*_*ity 58

有一个解决方法.如果你使用

XmlSerializer lizer = XmlSerializer.FromTypes(new[] { typeof(MyType) })[0];
Run Code Online (Sandbox Code Playgroud)

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

警告: 不要多次使用,否则会有内存泄漏

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

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

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

  • **警告:**如果您使用此方法多次为同一类型创建`XmlSerializer`实例,您将会像疯了一样泄漏内存!这是因为这个方法绕过了内置的缓存,只要`XmlSerializer(type)`和`XmlSerializer(type,defaultNameSpace)`构造函数(所有其他构造函数也绕过缓存).如果你使用任何方法创建一个不通过这两个构造函数的`XmlSerializer`,你必须实现自己的缓存或者你会出血. (44认同)
  • 很好的解决方法.这应该被接受为答案! (4认同)
  • @AllonGuralnek嗯,我会被诅咒......你是绝对正确的; 通过Reflector进一步挖掘显示,虽然它确实检查了缓存,但在**生成序列化程序集后它会这样做**!跆拳道?!? (4认同)
  • 原来是一个已知的错误:http://weblogs.asp.net/cschittko/archive/2005/01/14/353435.aspx (4认同)
  • @JerKimball:那个页面实际上并没有说谎.正如您所发现的那样,`FromTypes`似乎确实填充了缓存.所以它应该是一个有效的方法来在一个语句中预热一个空的`XmlSerializer`缓存(如文章建议的那样),但是从它检索任何东西的一种非常糟糕的方法(应该只通过最简单的构造函数完成).在任何情况下,我都不知道这是一个bug,我一直认为泄漏的任何东西都应该泄漏(就像更先进的`XmlSerializer`构造函数).我甚至不会考虑使用`FromTypes()`因为你可以做`types.Select(t => new XmlSerializer(t))`. (3认同)
  • @AllonGuralnek使用"FromTypes"的非探测方面确实具有吸引力 - 即使抛出的异常都被捕获,它也是一个价格合理的操作; "缓存你自己的方式"方法似乎是唯一的解决方法,因为唯一正式支持的修复看起来是在一个模糊的基于Web的程序集中.(编辑:坦率地说,我全都将数据合同移植到数据合同:)) (2认同)

小智 21

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

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

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
Run Code Online (Sandbox Code Playgroud)

对此:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T).GetNestedTypes());
Run Code Online (Sandbox Code Playgroud)

解决了我的问题.没有更多例外或任何事情.

  • 这对我有用.使用.Net4.0格式为`var xmlSerializer = new XmlSerializer(typeof(T),typeof(T).GetNestedTypes()); ` (8认同)
  • 如果多次运行,这也会产生内存泄漏。 (2认同)

d--*_*--b 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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ami*_*Bar 7

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

  1. 将属性添加到序列化类(我希望您有权访问)
  2. 使用sgen.exe生成序列化文件

将System.Xml.Serialization.XmlSerializerAssembly属性添加到您的类.将'MyAssembly'替换为MyClass所在的程序集的名称.

[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{
…
}
Run Code Online (Sandbox Code Playgroud)

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

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

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


Hir*_*ind 6

此异常也可以由名为BindingFailure 的托管调试助手(MDA)捕获.

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

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


Tom*_*bes 6

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

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

XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));
Run Code Online (Sandbox Code Playgroud)

工厂看起来像:

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;
    }       
}
Run Code Online (Sandbox Code Playgroud)

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

    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;
    }       
}
Run Code Online (Sandbox Code Playgroud)