XmlSerializer在构造函数中提供FileNotFoundException


347

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

像这样的声明

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)

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

我该如何解决这个问题?


5
好的,所以这个问题只是我已经问过的VB问题的C#版本:stackoverflow.com/questions/294659 / ... 谢谢大家。
Irwin,2009年

1
六年过去了,@ VladV的答案是最简单,影响最小的解决方案。只需将Generate serialization assembly下拉菜单更改为“开”,而不是“自动”即可。
Heliac

@Heliac:我不同意。它并不总是有效。请参阅Benoit Blanchon对Vlad的回答的评论。对我来说,最简单的答案是不要在配置文件中使用String.Collection。相反,我使用:string []项目= Settings.Default.StringofNewlineDelimitedItems.Split(new [] {Environment.NewLine});
Andrew Dennison 2015年

Answers:


388

信不信由你,这是正常的行为。引发了一个异常,但由XmlSerializer处理,因此,如果您忽略它,则一切都应继续进行。

我发现这很烦人,如果您进行一些搜索,就会有很多关于此的投诉,但是据我了解,Microsoft并不打算对此做任何事情。

如果您为特定异常关闭了初次机会异常,则可以避免在调试时始终获得异常弹出窗口。在Visual Studio中,转到“ 调试” ->“ 异常”(或按Ctrl+ Alt+ E),“ 公共语言运行时异常” ->“ System.IO” ->“ System.IO.FileNotFoundException”

您可以在博客文章C#XmlSerializer FileNotFound异常(讨论Chris Sells的工具XmlSerializerPreCompiler)中找到有关它的另一种方法的信息。


162
解决此问题的一种可能方法是在“工具”->“选项”->“调试”->“常规”选项中选中“仅我的代码”选项。
Frederic

26
@Frederic:这个评论很棒!我坐在这里的是“ WTF !!” 表情,试图寻找这种虚假的异常,然后我找到了这个问题,并给出了答案(这是微软的错,还有什么新功能吗?),但我不想禁用异常处理,因为我可能需要使用它我的代码。A +!
库巴(Kumba)

27
我认为以下Hans的建议更有价值-使用完全不会产生此异常的不同方法调用:XmlSerializer serializer = XmlSerializer.FromTypes(new [] {typeof(MyType)})[0];
明亮的

3
问题是,这使我的测试失败了,所以我不能仅“忽略”异常
Csaba Toth 2013年

16
对不起,但这是一个可怕的建议。根据我的经验,FileNotFoundException是较常见的一种,禁用此异常报告只是在将来某天带来麻烦。最好打开“仅我的代码”或启用以下所述的序列化程序集的创建。
Quarkly

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)继续执行程序,一切都会好起来。如果您对停止程序执行并弹出异常帮助程序的异常感到不安,则可以关闭“仅我的代码”,或者将FileNotFoundException设置为在引发时中断执行,而不是在“ User-未处理”。

要启用“仅我的代码”,请转到工具>>选项>>调试>>常规>>启用仅我的代码。要在抛出FileNotFound时关闭执行中断功能,请转至调试>>异常>>查找>>输入'FileNotFoundException'>>取消选中System.IO.FileNotFoundException中的“ Thrown”复选框。


+1更新:这解释了调试测试用例时的不同行为
mbx

3
您的更新建议此异常不应在.NET 4.5中发生,但我仍然看到它。
2015年

@Timbo:我不明白为什么您不会在.NET 4.5中遇到该异常。它仍然会寻找文件,如果文件丢失,FileNotFoundException将抛出a。区别不在于如何检查程序集的存在,而是在于一旦确定缺少该程序集就如何生成它。以前,它使用文本C#代码生成以及对C#编译器的调用来创建IL。从.NET 4.5开始,它直接发出IL,而无需使用编译器。
Allon Guralnek

1
我只是希望MS能够像(File.Exists(...)){Load} else {Fallback}那样实现,而不是尝试{Load} catch {Fallback}。基于异常的流控制有不好的气味,使我的调试经验变得更加困难和脆弱。
廷博

1
@Timbo:简单File.Exists()可能还不够。定位程序集并不是一件容易的事,运行时会在多个位置中查找,并且我相信行为会根据环境而变化(控​​制台应用程序与IIS中托管的应用程序等)。我猜应该执行的是a TryLoadAssembly()或类似内容。
Allon Guralnek,2015年


59

有一个解决方法。如果您使用

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

它应该避免该异常。这对我有用。

警告: 请勿多次使用,否则会导致内存泄漏

如果使用此方法XmlSerializer多次创建同一类型的实例,您将疯狂地泄漏内存!

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

如果您使用任何不通过这两个构造函数的方法来创建XmlSerializer,则必须实现自己的缓存,否则会浪费内存。


44
警告:如果使用此方法XmlSerializer多次创建相同类型的实例,将会疯狂地泄漏内存!这是因为此方法绕过了XmlSerializer(type)XmlSerializer(type, defaultNameSpace)构造函数提供的内置缓存(所有其他构造函数也绕过了缓存)。如果您使用任何方法XmlSerializer通过这两个构造函数创建的方法都不行,则必须实现自己的缓存,否则会浪费内存。
Allon Guralnek,2012年

4
@AllonGuralnek好吧,我该死的……你是绝对正确的。通过Reflector进行的进一步挖掘显示,虽然它确实检查了缓存,但是生成序列化程序集之后进行了检查!WTF?!?
JerKimball 2013年


3
@JerKimball:该页面实际上不是在撒谎。正如您所发现的,FromTypes确实确实会填充缓存。因此,它应该是XmlSerializer在一个语句中预热空缓存的有效方法(如文章所述),但是从其中检索任何内容的方法却非常糟糕(只能通过最简单的构造函数完成)。无论如何,我都不知道这是一个错误,我一直认为泄漏应该泄漏的任何东西(例如更高级的XmlSerializer构造函数)。我什至不会考虑使用,FromTypes()因为您可以这样做types.Select(t => new XmlSerializer(t))
Allon Guralnek

2
@AllonGuralnek使用的非探测方面FromTypes确实具有吸引力-即使抛出的异常都被捕获,这是一项昂贵的操作;“唯一的缓存方式”似乎是唯一的解决方法,因为唯一受官方支持的修复程序似乎是在晦涩的基于Web的程序集中。(编辑:坦白地说,我全都将所有内容移植到数据合约中了:))
JerKimball 2013年

22

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

然后我终于找到了解决方案。看来,序列化器不仅需要类型,而且还需要嵌套类型。改变这个:

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

对此:

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

为我解决了此问题。没有更多的例外或任何东西。


8
这对我有用。使用.Net4.0的格式为var xmlSerializer = new XmlSerializer(typeof(T), typeof(T).GetNestedTypes());
user3161729,2016年

1
这也为我工作。但是似乎仅在序列化时才需要,而在反序列化时似乎没有必要。也许这是有道理的,也许没有。
SteveCinq '18年

2
如果运行很多次,也会产生内存泄漏。
Volodymyr Kotylo

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

这里有两个问题-首先,您的代码不是线程安全的,其次(更重要的是)您正在尝试复制.net运行时已经执行的操作(基于所使用的ctor)。即不需要此代码
Dave Black

@DaveBlack:是的,将四边形与缓存到ConcurrentDictionary的答案会更好
d–b

@db我的第二点是,甚至不需要缓存-只要您使用的是框架缓存的两个ctor中的一个(OP使用的是第一个)。来自MSDN:为了提高性能,XML序列化基础结构动态生成程序集以序列化和反序列化指定的类型。框架查找并重用这些程序集。使用以下构建函数时,才会出现这种情况:XmlSerializer.XmlSerializer(类型)XmlSerializer.XmlSerializer(类型,字符串)参考:msdn.microsoft.com/en-us/library/...
戴维黑

@DaveBlack:是的,但是即使使用完全有效,这些构造函数也会在内部引发并捕获异常。这很糟糕,这就是OP首先问这个问题的原因。
d–b

@db是的,但是我想说的是(但不清楚-我很抱歉),您的soln唯一需要的行是else条件中的前3行。
戴夫·布莱克

8

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

  1. 向序列化的类添加属性(希望您可以访问)
  2. 使用sgen.exe生成序列化文件

将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上运行


好的,如果没有这些更改,它是否会失败?如果是,为什么?
约翰·桑德斯

1
我找不到为什么我的项目(VS2012中的4.0)突然开始失败的原因。“忽略”错误不是一种选择,因为每次我尝试访问Active Directory时都会发生。因此,忽略将意味着不进行身份验证。我仍然对VS2012无法正确自动生成序列化DLL感到非常沮丧。但是,这些步骤提供了完美的解决方案。
sfuqua 2013年

6

托管调试助手也可以捕获此异常称为BindingFailure(MDA)。

如果您的应用程序设计为附带预构建序列化程序集,则此MDA很有用。我们这样做是为了提高应用程序的性能。它使我们能够确保在构建过程中正确构建了预构建的序列化程序集,并在不进行即时重新构建的情况下由应用程序加载了该序列化程序集。

除非在这种情况下,否则它实际上没有用,因为正如其他张贴者所说的那样,当Serializer构造函数捕获绑定错误时,将在运行时重新构建序列化程序集。因此,您通常可以将其关闭。


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

您应该改用ConcurrentDictionary。此代码可能会死锁。
Behrooz

如果所有带有字典的管理都在锁定区域,怎么会死锁?
Tomas Kubes

对不起,我感到困惑。我的意思是,它可以多次插入一个项目。因为它在检查是否存在与插入之间存在差距。并发字典使用某种两阶段锁定(bag [0],然后再为bag [hash]]),并保留对必须插入/包含您正在使用的项目的bag的引用。它更快,更安全,更清洁。
Behrooz

是的,没有。没错,有可能在同一时间在两个线程上并行创建一个相同类型的序列化器,然后两次将其添加到字典中。在这种情况下,第二个插件将仅替换第一个插件,但是锁定部分保证了线程安全,总体缺点是内存泄漏少。这是性能优化,因为在实际场景中,您不希望具有A型序列化器的线程一被具有B型序列化器的线程二阻塞。
Tomas Kubes,

我可以想象该解决方案可能更好(没有理论上的内存泄漏),但是更加复杂。
Tomas Kubes

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在无法访问temp目录的帐户或安全环境下运行时,也会发生此错误。

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


他没有指定这是在运行时发生的。我能想到的另一件事是您可能存在名称空间/类冲突。您的MyType的全名是什么?
Zyphrax

是的,我检查了您的链接,有关构造函数的信息虽然有用,但不是我所需要的。
Irwin,2009年

5
@SpaceghostAl您可以在运行时进行编译。这就是XmlSerializer所做的。它在运行时动态构造一个程序集,该程序集可对特定类型的XML(反序列化)。无论出于何种原因,此过程对于OP都会失败。可能是因为权限问题,例如在临时目录上。(甚至可能像磁盘空间不足一样愚蠢。)
Nos

你确定吗?我非常确定,在构建过程中不会将序列化内容编译为名称为YourAssemblyName.XmlSerializers.dll的程序集,而不是在运行时进行编译。由于种种原因(包括部署文件夹中的所有NTFS权限),该操作可能会失败。
tomfanning

1
我希望我可以多次投票。您关于帐户无法访问temp文件夹的注释为我触发了答案。将服务帐户添加到服务器上的admin组后,它就可以正常工作。谢谢!
鲍勃·霍恩

2

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


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

2
-1表示没有使用块以防止资源泄漏,并且用于XmlTextWriter。
约翰·桑德斯

好的,但是我仍然使用XmlSerializer xmlSerializer = new XmlSerializer(typeof(TestClass)); 但我没有得到所说的例外。
shahjapan

1

我遇到了类似的问题,并且忽略该异常对我不起作用。我的代码正在调用NServiceBus的配置Configure.With(...).XmlSerializer()...

对我来说固定的是更改项目平台。

  1. 转到Build \ Configuration Manager ...
  2. 查找您的项目并更改平台(在我的情况下,从x86更改为Any CPU)

1

仅供参考。从数据库的答案和评论中,我得到了与数据库解决方案非常接近的此解决方案。在我所有情况下,它都能正常工作,并且是线程安全的。我不认为使用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);
                }
            }
        }

0

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

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

您能否举一个要序列化类型的例子?

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


0

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


0

在使用第3方工具从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
  {
  ...

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.