web-dev-qa-db-fra.com

XmlSerializer donnant FileNotFoundException au constructeur

Une application avec laquelle je travaille échoue lorsque j'essaie de sérialiser des types.

Une déclaration comme

XmlSerializer lizer = new XmlSerializer(typeof(MyType));

produit:

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)

Je ne définis aucun sérialiseur spécial pour ma classe.

Comment puis-je résoudre ce problème?

336
Irwin

Croyez-le ou non, c'est un comportement normal. Une exception est levée mais gérée par XmlSerializer. Par conséquent, si vous l'ignorez, tout devrait continuer normalement.

J'ai trouvé cela très angoissant et il y a eu de nombreuses plaintes à ce sujet si vous effectuez une recherche un peu, mais d'après ce que j'ai lu, Microsoft ne prévoit rien faire à ce sujet.

Si vous désactivez les exceptions de la première chance pour cette exception spécifique, vous pouvez éviter de recevoir tout le temps des fenêtres contextuelles sur les exceptions. Dans Visual Studio, accédez à Debug -> Exceptions (ou appuyez sur Ctrl + Alt + E), Exceptions du langage commun -> System.IO -> System.IO.FileNotFoundException .

Vous pouvez trouver des informations sur un autre moyen de le contourner dans l'article du blog exception C # XmlSerializer FileNotFound (outil de Chris Sells XmlSerializerPreCompiler ).

372
Martin Sherburn

Comme l'a dit Martin Sherburn, il s'agit d'un comportement normal. Le constructeur de XmlSerializer essaie d'abord de trouver un assembly nommé [YourAssembly] .XmlSerializers.dll qui devrait contenir la classe générée pour la sérialisation de votre type. Comme une telle DLL n'a pas encore été générée (ils ne sont pas par défaut), une exception FileNotFoundException est levée. Lorsque cela se produit, le constructeur de XmlSerializer intercepte cette exception et la DLL est générée automatiquement au moment de l'exécution par le constructeur de XmlSerializer (ceci est effectué en générant des fichiers source C # dans le répertoire% temp% de votre ordinateur, puis en les compilant. en utilisant le compilateur C #). Des constructions supplémentaires d'un XmlSerializer pour le même type utiliseront simplement la DLL déjà générée.

UPDATE: À partir de .NET 4.5, XmlSerializer n'effectue plus la génération de code ni la compilation avec le compilateur C # afin de créer un serializer Assembly au moment de l'exécution, sauf obligation explicite en définissant un paramètre de fichier de configuration ( seLegacySerializerGeneration ). Cette modification supprime la dépendance sur csc.exe et améliore les performances de démarrage.  Source: fichier Lisez-moi. NET Framework 4.5 , section 1.3.8.1.

L'exception est gérée par le constructeur de XmlSerializer. Il n'y a pas besoin de faire quoi que ce soit vous-même, vous pouvez simplement cliquer sur 'Continuer' (F5) pour continuer à exécuter votre programme et tout ira bien. Si les exceptions arrêtant l'exécution de votre programme et affichant un assistant d'exception vous dérangent, vous avez soit l'option 'Just My Code' désactivée, soit l'option FileNotFoundException définie pour interrompre l'exécution lorsqu'elle est lancée, au lieu de 'User- non pris en charge '.

Pour activer "Just My Code", accédez à Outils >> Options >> Débogage >> Général >> Activer Just My Code. Pour désactiver l'interruption de l'exécution lorsque FileNotFound est lancé, accédez à Débogage >> Exceptions >> Rechercher >> entrez "FileNotFoundException" >> décochez la case à cocher "Lancé" de System.IO.FileNotFoundException.

101
Allon Guralnek

Dans les propriétés du projet Visual Studio (page "Build", si je me souviens bien), il existe une option indiquant "générer l'assemblage de sérialisation". Essayez de l'activer pour un projet qui génère (contenant l'ensemble de MyType].

63
VladV

Il existe une solution de contournement pour cela. Si tu utilises

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

il devrait éviter cette exception. Cela a fonctionné pour moi.

ATTENTION: N'utilisez pas plusieurs fois, sinon vous aurez un fuite de mémoire

Vous perdrez de la mémoire comme un fou si vous utilisez cette méthode pour créer plusieurs fois des instances de XmlSerializer pour le même type!

En effet, cette méthode contourne la mise en cache intégrée à condition que les constructeurs XmlSerializer(type) et XmlSerializer(type, defaultNameSpace) (tous les autres constructeurs contournent également le cache).

Si vous utilisez une méthode pour créer un XmlSerializer qui n'est pas via ces deux constructeurs, vous devez implémenter votre propre mise en cache ou votre mémoire d'hémorragie.

58
quadfinity

Je suis tombé sur ce problème et je ne pouvais pas le contourner par aucune des solutions mentionnées.

Puis j'ai finalement trouvé une solution. Il semble que le sérialiseur ait besoin non seulement du type, mais également des types imbriqués. Changer cela:

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

Pour ça:

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

Correction du problème pour moi. Pas plus d'exceptions ou quoi que ce soit.

21
Frosty

Ma solution est d'aller directement à la réflexion pour créer le sérialiseur. Cela contourne le chargement de fichier étrange qui provoque l'exception. Je l'ai intégré dans une fonction d'assistance qui prend également en charge la mise en cache du sérialiseur.

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;
    }
}
9
d--b

Pour éviter l'exception, vous devez faire deux choses:

  1. Ajouter un attribut à la classe sérialisée (j'espère que vous avez accès)
  2. Générez le fichier de sérialisation avec sgen.exe

Ajoutez l'attribut System.Xml.Serialization.XmlSerializerAssembly à votre classe. Remplacez "MyAssembly" par le nom de l’ensemble où se trouve MyClass.

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

Générez le fichier de sérialisation à l'aide de l'utilitaire sgen.exe et déployez-le avec l'assembly de la classe.

'Sgen.exe MyAssembly.dll' générera le fichier MyAssembly.XmlSerializers.dll.

Ces deux modifications vont amener le .net à trouver directement l’Assemblée. Je l'ai vérifié et cela fonctionne sur .NET Framework 3.5 avec Visual Studio 2008

7
Ami Bar

Cette exception peut également être interceptée par un assistant de débogage géré (MDA) appelé BindingFailure.

Ce MDA est utile si votre application est conçue pour être livrée avec des assemblys de sérialisation pré-construits. Nous faisons cela pour augmenter les performances de notre application. Cela nous permet de nous assurer que les assemblys de sérialisation prédéfinis sont correctement construits par notre processus de construction et chargés par l'application sans être reconstruits à la volée.

Ce n'est vraiment pas utile, sauf dans ce scénario, car, comme d'autres afficheurs l'ont dit, lorsqu'une erreur de liaison est interceptée par le constructeur Serializer, l'assembly de sérialisation est reconstruit à l'exécution. Ainsi, vous pouvez généralement l'éteindre.

6
HiredMind

La fonction XmlSerializer.FromTypes ne lève pas l'exception, mais perd de la mémoire. C’est la raison pour laquelle vous devez mettre en cache ce sérialiseur pour chaque type afin d’éviter les fuites de mémoire pour chaque instance créée.

Créez votre propre usine XmlSerializer et utilisez-la simplement:

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

L'usine ressemble aime:

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

Version plus compliquée sans possibilité de fuite de mémoire (veuillez vérifier le code):

    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;
    }       
}
6
Tomas Kubes

En revanche, le dépannage des erreurs de compilation est très compliqué. Ces problèmes se manifestent dans une exception FileNotFoundException avec le message:

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

Vous vous demandez peut-être ce qu’une exception fichier non trouvé a à voir avec l’instanciation d’un objet sérialiseur, mais rappelez-vous: le constructeur écrit des fichiers C # et essaie de les compiler. La pile d'appels de cette exception fournit de bonnes informations pour supporter cette suspicion. L'exception s'est produite lorsque XmlSerializer a tenté de charger un assembly généré par CodeDOM en appelant la méthode System.Reflection.Assembly.Load. L'exception ne fournit pas d'explication sur la raison pour laquelle l'assembly que XmlSerializer était supposé créer n'était pas présent. En général, l'assembly n'est pas présent car la compilation a échoué, ce qui peut se produire car, dans de rares circonstances, les attributs de sérialisation produisent un code que le compilateur C # ne parvient pas à compiler.

Remarque Cette erreur se produit également lorsque XmlSerializer s'exécute sous un compte ou dans un environnement de sécurité ne pouvant pas accéder au répertoire temporaire.

Source : http://msdn.Microsoft.com/en-us/library/aa302290.aspx

3
Zyphrax

Dans les propriétés de projet Visual Studio, une option indique "générer l'assemblage de sérialisation". Essayez de l'activer pour un projet qui génère [Containing Assembly of MyType].

2
Pascal

J'avais un problème similaire et ignorer l'exception ne fonctionnait pas pour moi. Mon code appelait la configuration de NServiceBus Configure.With(...).XmlSerializer()...

Ce qui a résolu le problème pour moi était de changer la plate-forme de mon projet.

  1. Accédez à Build\Configuration Manager ...
  2. Trouvez votre projet et changez de plate-forme (dans mon cas, de x86 à n'importe quel processeur)
1
kkelley

Une classe personnalisée à sérialiser:

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

J'ai joint l'extrait de code. Peut-être que cela peut vous aider.

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

Juste comme référence. Partant de la réponse et des commentaires de D-B, je suis arrivé avec cette solution qui est proche de la solution de D-B. Cela fonctionne bien dans tous mes cas et il est thread-safe. Je ne pense pas que l'utilisation d'un ConcurrentDictionary aurait été ok.

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

Usage:

        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);
                }
            }
        }
1
Eric Ouellet

J'ai eu le même problème jusqu'à ce que j'ai utilisé un outil tiers pour générer la classe à partir du XSD et cela a fonctionné! J'ai découvert que l'outil ajoutait du code supplémentaire en haut de ma classe. Lorsque j'ai ajouté ce même code au sommet de ma classe d'origine, cela a fonctionné. Voici ce que j'ai ajouté ...

#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
TheJonz

Vu beaucoup de recommandations pour utiliser un ConcurrentDictionary, mais aucun exemple concret, alors je vais me lancer dans cette course à la solution. Je ne suis pas un développeur thread-safe, donc si ce code n'est pas solide, veuillez en parler pour le bien de ceux qui suivront.

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

J'ai vu d'autres publications impliquant ConcurrentDictionary et Lazy chargeant la valeur. Je ne sais pas si c'est pertinent ici ou non, mais voici le code pour cela:

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

Votre type peut faire référence à d'autres assemblys qui ne peuvent être trouvés ni dans le GAC ni dans votre dossier bin local ==> ...

"ou une de ses dépendances. Le système ne peut pas trouver le fichier spécifié"

Pouvez-vous donner un exemple du type que vous souhaitez sérialiser?

Remarque: Assurez-vous que votre type implémente Serializable.

0
Henrik

J'obtenais la même erreur, et cela était dû au type que j'essayais de désérialiser, n'ayant pas de constructeur sans paramètre par défaut. J'ai ajouté un constructeur et ça a commencé à fonctionner.

0
kay.one