XmlSerializer在构造函数中给出FileNotFoundException

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

像这样的陈述

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)

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

我该如何解决这个问题?

138458 次浏览

另一方面,对编译错误进行故障排除是非常复杂的。这些问题在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()

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

< >强注 当XmlSerializer在不能访问临时目录的帐户或安全环境下运行时,也会发生此错误 < p > : http://msdn.microsoft.com/en-us/library/aa302290.aspx < / p >

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

您的类型可以引用其他程序集,这些程序集既不能在广汽中找到,也不能在您的本地bin文件夹==>…

"或它的依赖项之一。该系统 “

.

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

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

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

我发现这非常烦人,如果你搜索一下,会有很多抱怨,但据我所知,微软并不打算对此采取任何措施。

如果关闭特定异常的第一次机会异常,就可以避免在调试时一直弹出异常。在Visual Studio中,转到调试 ->异常(或按Ctrl + Alt + E) 公共语言运行时异常系统。IOSystem.IO.FileNotFoundException

你可以在博客文章 c# XmlSerializer FileNotFound exception中找到关于另一种方法的信息(其中讨论了Chris Sells的工具XmlSerializerPreCompiler)。

要序列化的自定义类:

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

就像马丁·舍伯恩说的,这是正常行为。XmlSerializer的构造函数首先尝试找到一个名为[YourAssembly].XmlSerializer .dll的程序集,该程序集应该包含用于序列化您的类型的生成类。由于这样的DLL还没有生成(默认情况下没有生成),因此抛出FileNotFoundException异常。当发生这种情况时,XmlSerializer的构造函数捕捉到该异常,并且由XmlSerializer的构造函数在运行时自动生成DLL(这是通过在计算机的%temp%目录中生成c#源文件,然后使用c#编译器编译它们来完成的)。相同类型的XmlSerializer的其他构造将只使用已经生成的DLL。

更新:从。net 4.5开始,XmlSerializer不再执行代码生成,也不再使用c#编译器在运行时创建序列化程序集,除非通过设置配置文件设置(useLegacySerializerGeneration)显式强制执行。这个改变消除了对csc.exe的依赖,提高了启动性能。来源:。. NET Framework 4.5 Readme, section 1.3.8.1. . NET Framework

异常由XmlSerializer的构造函数处理。你不需要自己做任何事情,你可以点击“继续”(F5)继续执行你的程序,一切都会好的。如果你被异常停止程序执行并弹出异常助手所困扰,你要么关闭“只是我的代码”,要么将FileNotFoundException设置为在抛出时中断执行,而不是在“User-unhandled”时。

打开“Just My Code”,请转到“Tools >> Options >> Debugging >> General >> enable Just My Code”。要关闭FileNotFound被抛出时的执行中断,请转到调试>>异常>>找到>>输入'FileNotFoundException' >>取消System.IO.FileNotFoundException中的' thrown '复选框。

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

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

除了在这种情况下,它实际上没什么用,因为正如其他帖子所说,当绑定错误被Serializer构造函数捕获时,序列化程序集将在运行时重新构建。所以你可以把它关掉。

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

  1. 向序列化类添加属性(我希望您有访问权限)
  2. 使用sgen.exe生成序列化文件
将System.Xml.Serialization.XmlSerializerAssembly属性添加到类中。 将'MyAssembly'替换为MyClass所在程序集的名称
[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{
…
}

使用sgen.exe实用程序生成序列化文件,并将其与类的程序集一起部署。

' sgen.exe MyAssembly.dll '将生成文件myassembly . xmlserializer .dll

这两个更改将导致.net直接找到程序集。 我检查了一下,它可以在。net framework 3.5和Visual Studio 2008上工作

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

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

我的解决方案是直接使用反射来创建序列化器。这就绕过了导致异常的奇怪文件加载。我将其打包在一个helper函数中,该函数还负责缓存序列化器。

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

有一个变通的办法。如果你使用

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

它应该避免这种异常。这对我很管用。

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

如果你使用这个方法为同一类型创建XmlSerializer的实例不止一次,你将会疯狂地泄漏内存!

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

如果使用任何方法来创建XmlSerializer,而不是通过这两个构造函数,那么必须实现自己的缓存,否则会导致内存泄漏。

我也有同样的问题,直到我使用第三方工具从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
{
...

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

对我来说,解决这个问题的方法是改变我项目的平台。

  1. 去构建\配置管理器…
  2. 找到你的项目并更改平台(在我的情况下,从x86到任何CPU)

我遇到了这个问题,上面提到的任何解决方案都无法解决它。

然后我终于找到了一个解决方案。 序列化器似乎不仅需要类型,还需要嵌套类型。 改变:< / p >
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

:

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

为我修复了这个问题。

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

只是作为参考。从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);
}
}
}

见过很多使用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;
}

在我的一个。net标准dll中也有类似的问题。

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