XmlSerializer donnant une exception FileNotFoundException au constructeur

Une application avec laquelle j’ai travaillé échoue lorsque j’essaie de sérialiser les 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, Ssortingng codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) at System.Reflection.Assembly.nLoad(AssemblyName fileName, Ssortingng codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 

Je ne définis pas de sérialiseurs spéciaux pour ma classe.

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

Croyez-le ou non, c’est un comportement normal. Une exception est levée mais gérée par le XmlSerializer, donc si vous l’ignorez, tout devrait continuer correctement.

J’ai trouvé cela très ennuyeux, et il y a eu beaucoup de plaintes à ce sujet si vous cherchez un peu, mais d’après ce que j’ai lu, Microsoft n’envisage rien à ce sujet.

Vous pouvez éviter de recevoir des fenêtres contextuelles Exception lors du débogage si vous désactivez les exceptions de première chance pour cette exception spécifique. Dans Visual Studio, accédez à Débogage -> Exceptions (ou appuyez sur Ctrl + Alt + E ), Exceptions Common Language Runtime -> System.IO -> System.IO.FileNotFoundException .

Vous pouvez trouver des informations sur une autre manière de contourner le problème dans l’exception C # XmlSerializer FileNotFound de l’article sur le blog (l’outil XmlSerializerPreComstackr de Chris Sells).

Comme Martin Sherburn l’a dit, c’est un comportement normal. Le constructeur du XmlSerializer essaie d’abord de trouver un assembly nommé [YourAssembly] .XmlSerializers.dll qui doit 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 (elles ne sont pas par défaut), une exception FileNotFoundException est lancée. Lorsque cela se produit, le constructeur de XmlSerializer intercepte cette exception et la DLL est générée automatiquement à l’exécution par le constructeur de XmlSerializer (cela se fait en générant des fichiers source C # dans le répertoire% temp% de votre ordinateur, puis en les compilant). Les constructions supplémentaires d’un XmlSerializer pour le même type utiliseront simplement la DLL déjà générée.

MISE À JOUR: À partir de .NET 4.5, XmlSerializer n’effectue plus la génération de code ni la compilation avec le compilateur C # pour créer un assembly sérialiseur à l’exécution, à moins d’y être contraint en définissant un paramètre de fichier de configuration ( useLegacySerializerGeneration ). Cette modification supprime la dépendance sur csc.exe et améliore les performances de démarrage. Source: Lisez – moi de .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 vous êtes gêné par les exceptions qui arrêtent l’exécution de votre programme et que vous ouvrez un assistant d’exception, vous pouvez désactiver «Just My Code» ou définir une exception FileNotFoundException pour interrompre l’exécution au lieu de non manipulé ».

Pour activer ‘Just My Code’, allez dans Outils >> Options >> Débogage >> Général >> Activer juste mon code. Pour désactiver la fin de l’exécution lorsque FileNotFound est lancé, accédez à Déboguer >> Exceptions >> Rechercher >> entrez «FileNotFoundException» >> décochez la case «Thrown» de System.IO.FileNotFoundException.

Dans les propriétés du projet Visual Studio (page “Build”, si je me souviens bien), une option indique “Générer un assemblage de sérialisation”. Essayez de l’activer pour un projet qui génère [Containing Assembly of MyType] .

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

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

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

AVERTISSEMENT: n’utilisez pas plusieurs fois, ou vous aurez une fuite de mémoire

Vous allez perdre la mémoire comme un fou si vous utilisez cette méthode pour créer des instances de XmlSerializer pour le même type plus d’une fois!

En effet, cette méthode contourne la mise en cache intégrée fournie par 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 vous allez hémorragier la mémoire.

Je suis tombé sur ce problème précis et je n’ai pu le contourner par aucune des solutions mentionnées.

Puis j’ai finalement trouvé une solution. Il semble que le sérialiseur nécessite non seulement le type, mais aussi les types nesteds. Changer ceci:

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

Pour ça:

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

Correction du problème pour moi Plus d’exceptions ou quoi que ce soit.

Ma solution consiste à aller directement à la reflection pour créer le sérialiseur. Cela contourne le chargement de fichier étrange à l’origine de l’exception. Je l’ai emballé dans une fonction d’assistance qui prend également en charge la mise en cache du sérialiseur.

 private static readonly Dictionary _xmlSerializerCache = new Dictionary(); 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; } } 

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

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

Ajoutez l’atsortingbut System.Xml.Serialization.XmlSerializerAssembly à votre classe. Remplacez «MyAssembly» par le nom de l’assembly dans lequel MyClass se trouve.

 [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’ va générer le fichier MyAssembly.XmlSerializers.dll

Ces deux modifications entraîneront la recherche directe de l’assembly par le .net. Je l’ai vérifié et ça marche sur .NET Framework 3.5 avec Visual Studio 2008

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

Cette MDA est utile si votre application est conçue pour être livrée avec des assemblages de sérialisation pré-construction. Nous faisons cela pour augmenter les performances de notre application. Cela nous permet de nous assurer que les assemblages de sérialisation pré-construits 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 affiches 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. Donc, vous pouvez généralement l’éteindre.

Fonction XmlSerializer.FromTypes ne lève pas l’exception, mais elle fuit la mémoire. C’est pourquoi vous devez mettre en cache un tel sérialiseur pour chaque type afin d’éviter toute fuite 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 à:

 public static class XmlSerializerFactoryNoThrow { public static Dictionary _cache = new Dictionary(); private static object SyncRootCache = new object(); ///  /// //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. ///  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 (s’il vous plaît quelqu’un revoir 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; } } 

Le dépannage des erreurs de compilation est en revanche 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.Comstackr.ComstackrResults.get_ComstackdAssembly() 

Vous pouvez vous demander ce qu’est une exception de fichier introuvable avec l’instanciation d’un object sérialiseur, mais rappelez-vous que le constructeur écrit les fichiers C # et essaie de les comstackr. La stack d’appels de cette exception fournit de bonnes informations pour prendre en charge cette suspicion. L’exception s’est produite pendant que le XmlSerializer tentait 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 le XmlSerializer était censé 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 atsortingbuts de sérialisation produisent du code que le compilateur C # ne parvient pas à comstackr.

Note Cette erreur se produit également lorsque le XmlSerializer s’exécute sous un compte ou un environnement de sécurité qui ne peut pas accéder au répertoire temporaire.

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

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

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(ssortingng[] 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; ssortingng xmlSerializedSsortingng = ConvertByteArray2Str(memoryStream.ToArray()); TestClass xmlDomain = (TestClass)DeserializeObject(xmlSerializedSsortingng); Console.WriteLine(xmlDomain.TestFunction().ToSsortingng()); Console.ReadLine(); } 

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 été résolu pour moi, c’est de changer la plate-forme de mon projet.

  1. Allez dans Build \ Configuration Manager …
  2. Trouvez votre projet et changez de plate-forme (dans mon cas de x86 à n’importe quel CPU)

Juste comme référence En prenant la réponse et les commentaires de la firebase database, je suis venu avec cette solution proche de la solution DB. Cela fonctionne bien dans tous mes cas et il est thread-safe. Je ne pense pas que l’utilisation d’un ConcurrentDictionary aurait été acceptable.

 using System; using System.Collections.Generic; using System.Xml.Serialization; namespace HQ.Util.General { public class XmlSerializerHelper { private static readonly Dictionary _dictTypeToSerializer = new Dictionary(); 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)x.Deserialize(reader); } catch (Exception ex) { Log.Instance.AddEntry(LogType.LogException, "Unable to open Options file: " + Path, ex); } } } 

Votre type peut faire référence à d’autres assemblages 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 trouve pas 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.

J’obtenais la même erreur, et c’était dû au type que je tentais de désérialiser sans avoir un constructeur sans paramètre par défaut . J’ai ajouté un constructeur et cela a commencé à fonctionner.

J’ai eu le même problème jusqu’à ce que j’utilise 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 au sumt de ma classe. Lorsque j’ai ajouté ce même code en haut de ma classe d’origine, cela fonctionnait. 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.Comstackr.GeneratedCodeAtsortingbute("System.Xml", "4.6.1064.2")] [System.SerializableAtsortingbute()] [System.Diagnostics.DebuggerStepThroughAtsortingbute()] [System.ComponentModel.DesignerCategoryAtsortingbute("code")] [System.Xml.Serialization.XmlTypeAtsortingbute(AnonymousType = true)] [System.Xml.Serialization.XmlRootAtsortingbute(Namespace = "", IsNullable = false)] public partial class MyClassName { ...