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)

Я не определяю никаких специальных сериализаторов для моего класса.

Как я могу исправить эту проблему?

Ответ 1

Верьте или нет, это нормальное поведение. Исключение вызывается, но обрабатывается XmlSerializer, поэтому, если вы просто игнорируете его, все должно продолжаться нормально.

Я нашел это очень анонимным, и было много жалоб на это, если вы немного повидались, но из того, что я читал, Microsoft не планирует ничего делать.

Во время отладки можно исключить всплывающие окна "Исключения", если вы отключите исключения для первого исключения для этого исключения. В Visual Studio перейдите в Debug → Exceptions (или нажмите Ctrl + Alt + E), исключения для обычного языка Runtime → System.IO → System.IO.FileNotFoundException.

Вы можете найти информацию о другом пути вокруг него в сообщении блога С# XmlSerializer FileNotFound exception (инструмент Chris Sells XmlSerializerPreCompiler).

Ответ 2

Как сказал Мартин Шерберн, это нормальное поведение. Конструктор XmlSerializer сначала пытается найти сборку с именем [YourAssembly].XmlSerializers.dll, которая должна содержать сгенерированный класс для сериализации вашего типа. Поскольку такая DLL еще не сгенерирована (они не по умолчанию), генерируется исключение FileNotFoundException. Когда это происходит, конструктор XmlSerializer ловит это исключение, а DLL генерируется автоматически во время выполнения конструктором XmlSerializer (это делается путем генерации исходных файлов С# в каталоге% temp% вашего компьютера, а затем их компиляции с использованием компилятора С#). Дополнительные конструкции XmlSerializer для того же типа будут использовать уже сгенерированную DLL.

UPDATE: Начиная с .NET 4.5, XmlSerializer больше не выполняет генерации кода и не выполняет компиляцию с компилятором С#, чтобы создать сборку сериализатора во время выполнения, если явно не принудительно установив настройку файла конфигурации (useLegacySerializerGeneration). Это изменение устраняет зависимость от csc.exe и улучшает производительность запуска. Источник: .NET Framework 4.5 Readme, раздел 1.3.8.1.

Исключение обрабатывается конструктором XmlSerializer. Вам не нужно ничего делать самостоятельно, вы можете просто нажать "Продолжить" (F5), чтобы продолжить выполнение своей программы, и все будет хорошо. Если вам мешают исключения, останавливающие выполнение вашей программы и выставляя вспомогательный элемент исключения, вы либо отключите "Just My Code", либо у вас есть установленное FileNotFoundException, чтобы разорвать выполнение при броске, а не когда "User- необработанное.

Чтобы включить "Только мой код", откройте "Инструменты → Параметры → Отладка → Общие → Включить только мой код. Чтобы отключить прерывание выполнения при подаче FileNotFound, перейдите в Debug → Exceptions → Find → enter 'FileNotFoundException' → отключите флажок" Брошенный" из System.IO.FileNotFoundException.

Ответ 3

В свойствах проекта Visual Studio (на странице "Build", если я правильно его помню) есть опция "генерировать сериализацию сборки". Попробуйте включить его для проекта, который генерирует [Содержащая сборку MyType].

Ответ 4

Для этого есть обходной путь. Если вы используете

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

он должен избегать этого исключения. Это сработало для меня.

ПРЕДУПРЕЖДЕНИЕ: Не используйте несколько раз, иначе у вас будет утечка памяти

Вы будете утечки памяти, как сумасшедшие, если вы используете этот метод для создания экземпляров XmlSerializer для одного и того же типа более одного раза!

Это связано с тем, что этот метод обходит встроенное кэширование, предоставляемое конструкторами XmlSerializer(type) и XmlSerializer(type, defaultNameSpace) (все остальные конструкторы также обходят кеш).

Если вы используете какой-либо метод для создания XmlSerializer, который не через эти два конструктора, вы должны реализовать свое собственное кэширование или вы будете кровоизнять память.

Ответ 5

Я столкнулся с этой проблемой и не мог обойти ее ни одним из упомянутых решений.

Тогда я наконец нашел решение. Похоже, что сериализатору нужен не только тип, но и вложенные типы. Меняем это:

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

На это:

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

Исправлена проблема для меня. Нет больше исключений или что-нибудь.

Ответ 6

Мое решение - перейти к размышлению, чтобы создать сериализатор. Это обходит загрузку странного файла, которая вызывает исключение. Я упаковал это в вспомогательную функцию, которая также занимается кэшированием сериализатора.

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

Ответ 7

Чтобы избежать исключения, вам нужно сделать две вещи:

  • Добавить атрибут в сериализованный класс (надеюсь, у вас есть доступ)
  • Создайте файл сериализации с помощью 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 напрямую находить сборку. Я проверил его, и он работает на .NET framework 3.5 с Visual Studio 2008

Ответ 8

Это исключение также может быть захвачено управляемым помощником по отладке (MDA), называемым BindingFailure.

Этот MDA полезен, если ваше приложение предназначено для доставки с сборками сериализации предварительной сборки. Мы делаем это для повышения производительности для нашего приложения. Это позволяет нам убедиться, что предварительно построенные сборки сериализации должным образом построены нашим процессом сборки и загружены приложением без переустановки на лету.

Это действительно не полезно, кроме как в этом сценарии, потому что, как говорили другие плакаты, когда ошибка привязки захватывается конструктором Serializer, сборка сериализации перестраивается во время выполнения. Поэтому вы обычно можете отключить его.

Ответ 9

Функция XmlSerializer.FromTypes не генерирует исключение, но оно утечки памяти. Вот почему вам необходимо кэшировать такой сериализатор для каждого типа, чтобы избежать утечки памяти для каждого созданного экземпляра.

Создайте свой собственный XmlSerializer factory и используйте его просто:

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

factory выглядит как:

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

Ответ 10

Устранение ошибок компиляции, с другой стороны, очень сложно. Эти проблемы проявляются в 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() 

Вы можете задаться вопросом, какое исключение, не найденное файлом, связано с созданием экземпляра объекта serializer, но помните: конструктор записывает файлы С# и пытается их скомпилировать. Стол вызовов этого исключения предоставляет некоторую хорошую информацию для подтверждения этого подозрения. Исключение произошло, когда XmlSerializer попытался загрузить сборку, сгенерированную CodeDOM, вызывающую метод System.Reflection.Assembly.Load. Исключение не дает объяснений, почему сборка, которую должен был создать XmlSerializer, отсутствовала. В общем случае сборки нет, потому что компиляция не удалась, что может произойти из-за того, что в редких случаях атрибуты сериализации производят код, который компилятор С# не удается скомпилировать.

Примечание Эта ошибка также возникает, когда XmlSerializer работает под учетной записью или средой безопасности, которая не может получить доступ к временному каталогу.

Источник: http://msdn.microsoft.com/en-us/library/aa302290.aspx

Ответ 11

В свойствах проекта Visual Studio есть опция, указывающая "сгенерировать сборку сериализации". Попробуйте включить его для проекта, который генерирует [Содержащая сборку MyType].

Ответ 12

Пользовательский класс для сериализации:

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

Ответ 13

У меня была аналогичная проблема, и игнорирование исключения не помогло мне. Мой код вызывал конфигурацию NServiceBus Configure.With(...).XmlSerializer()...

Для меня было исправлено изменение платформы для моего проекта.

  • Перейдите в Build\Configuration Manager...
  • Найдите свой проект и измените платформу (в моем случае от x86 до Any CPU)

Ответ 14

Как ссылка. Принимая ответы 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);
                }
            }
        }

Ответ 15

Ваш тип может ссылаться на другие сборки, которые не могут быть найдены ни в GAC, ни в вашей локальной папке bin == > ...

"или одной из его зависимостей. не может найти указанный файл"

Можете ли вы привести пример типа, который вы хотите сериализовать?

Примечание. Убедитесь, что ваш тип реализует Serializable.

Ответ 16

Я получал ту же ошибку, и это было связано с тем типом, который я пытался десериализовать, не имея конструктора без параметров без параметров. Я добавил конструктор и начал работать.

Ответ 17

У меня была такая же проблема, пока я не использовал инструмент третьей стороны для создания класса из 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
  {
  ...

Ответ 18

Было много рекомендаций по использованию 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);
        });
    }
}

Я видел другие посты, в которых ConcurrentDictionary и Lazy загружали значение. Я не уверен, уместно ли это здесь или нет, но вот код для этого:

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