XmlSerializer dando FileNotFoundException en el constructor

347

Una aplicación con la que he estado trabajando falla cuando intento serializar tipos.

Una declaración como

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

produce:

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)

No defino ningún serializador especial para mi clase.

¿Como puedo solucionar este problema?

Irwin
fuente
55
Bien, esta pregunta es solo mi versión C # de una pregunta de VB ya hecha: stackoverflow.com/questions/294659/... Gracias chicos.
Irwin el
1
Seis años después, la respuesta de @VladV es la solución más simple y menos perjudicial. Simplemente cambie el Generate serialization assemblymenú desplegable a "Activado", en lugar de "Automático".
Heliac
@Heliac: no estoy de acuerdo. No siempre funciona Vea el comentario de Benoit Blanchon a la respuesta de Vlad. La respuesta más simple para mí es no usar String.Collection en los archivos de configuración. En su lugar, uso: string [] items = Settings.Default.StringofNewlineDelimitedItems.Split (new [] {Environment.NewLine});
Andrew Dennison

Respuestas:

388

Lo creas o no, este es un comportamiento normal. Se produce una excepción, pero XmlSerializer la maneja, por lo que si la ignora, todo debería continuar bien.

Esto me pareció muy molesto, y ha habido muchas quejas sobre esto si buscas un poco, pero por lo que he leído, Microsoft no planea hacer nada al respecto.

Puede evitar las ventanas emergentes de excepción todo el tiempo mientras se depura si desactiva las excepciones de primera oportunidad para esa excepción específica. En Visual Studio, vaya a Depurar -> Excepciones (o presione Ctrl+ Alt+ E), Excepciones de Common Language Runtime -> System.IO -> System.IO.FileNotFoundException .

Puede encontrar información sobre otra forma de evitarlo en la publicación del blog C # XmlSerializer FileNotFound excepción (que analiza la herramienta XmlSerializerPreCompiler de Chris Sells ).

Martin Sherburn
fuente
162
Una de las formas posibles de deshacerse de este problema es marcar la opción "Solo mi código" en Herramientas -> Opciones -> Depuración -> Opciones generales.
Frederic
26
@Frederic: ¡Este comentario es increíble! Estoy sentado aquí con un "WTF !?" expresión en mi rostro, tratando de cazar esta espuria excepción, y encuentro esta pregunta, con respuesta (es culpa de Microsoft, ¿qué más hay de nuevo?), pero no quería deshabilitar el manejo de excepciones, porque podría necesitarlo para mi código. A +!
Kumba
27
Creo que la sugerencia de Hans a continuación es más valiosa: utilice una llamada a un método diferente que no produzca esta excepción en absoluto: XmlSerializer serializer = XmlSerializer.FromTypes (new [] {typeof (MyType)}) [0];
brillante
3
El problema es que esto falla mi prueba, por lo que no puedo simplemente "ignorar" la excepción
Csaba Toth
16
Lo siento, pero esta es una sugerencia terrible. FileNotFoundException es uno de los más comunes, en mi experiencia, y deshabilitar este informe de excepción es solo pedir problemas algún día en el futuro. Es mejor activar 'Just My Code' o habilitar la creación de los conjuntos de serialización que se describen a continuación.
Quarkly
104

Como dijo Martin Sherburn, este es un comportamiento normal. El constructor del XmlSerializer primero intenta encontrar un ensamblado llamado [YourAssembly] .XmlSerializers.dll que debe contener la clase generada para la serialización de su tipo. Dado que dicha DLL aún no se ha generado (no lo están de forma predeterminada), se genera una excepción FileNotFoundException. Cuando eso sucede, el constructor de XmlSerializer detecta esa excepción, y el constructor de XmlSerializer genera la DLL automáticamente en tiempo de ejecución (esto se hace generando archivos fuente C # en el directorio% temp% de su computadora, luego compilándolos usando el compilador C #). Las construcciones adicionales de un XmlSerializer para el mismo tipo solo usarán el DLL ya generado.

ACTUALIZACIÓN: a partir de .NET 4.5, XmlSerializerya no realiza la generación de código ni realiza la compilación con el compilador de C # para crear un ensamblado de serializador en tiempo de ejecución, a menos que se lo obligue explícitamente estableciendo una configuración de archivo de configuración ( useLegacySerializerGeneration ). Este cambio elimina la dependencia csc.exey mejora el rendimiento de inicio. Fuente: Léame de .NET Framework 4.5 , sección 1.3.8.1.

La excepción es manejada por el constructor de XmlSerializer. No hay necesidad de hacer nada usted mismo, simplemente haga clic en 'Continuar' (F5) para continuar ejecutando su programa y todo estará bien. Si le molestan las excepciones que detienen la ejecución de su programa y aparecen un asistente de excepción, tiene 'Just My Code' desactivado o tiene la FileNotFoundException configurada para interrumpir la ejecución cuando se lanza, en lugar de cuando 'User- no manejado ".

Para habilitar 'Solo mi código', vaya a Herramientas >> Opciones >> Depuración >> General >> Habilitar solo mi código. Para desactivar la interrupción de la ejecución cuando se lanza FileNotFound, vaya a Depurar >> Excepciones >> Buscar >> ingrese 'FileNotFoundException' >> desactive la casilla de verificación 'Lanzar' de System.IO.FileNotFoundException.

Allon Guralnek
fuente
+1 para la actualización: esto explica el comportamiento diferente al depurar casos de prueba
mbx
3
Su actualización sugiere que esta excepción no debería ocurrir en .NET 4.5, pero todavía la estoy viendo.
Timbo
@Timbo: No veo por qué no obtendrías esa excepción con .NET 4.5. Todavía busca un archivo, y si falta el archivo, se FileNotFoundExceptionlanzará un . La diferencia no está en cómo se verifica la existencia del ensamblaje, sino en cómo generarlo una vez que se determina que falta. Antes, usaba la generación de código textual de C # con una llamada al compilador de C # para crear el IL. Comenzando con .NET 4.5, emite IL directamente, sin el uso de un compilador.
Allon Guralnek
1
Solo deseo que MS implemente esto como si (File.Exists (...)) {Load} else {Fallback} en lugar de intentar {Load} catch {Fallback}. El control de flujo basado en excepciones huele mal y hace que mi experiencia de depuración sea más difícil y frágil de lo necesario.
Timbo
1
@Timbo: Un simple File.Exists()puede no ser suficiente. La ubicación de un ensamblaje no es un asunto simple, el tiempo de ejecución se ve en varias ubicaciones y creo que el comportamiento cambia de acuerdo con el entorno (aplicación de consola frente a ser alojado en IIS, etc.). Supongo que lo que debería haberse implementado fue un TryLoadAssembly()o algo similar.
Allon Guralnek
63

En las propiedades del proyecto de Visual Studio (página "Build", si no recuerdo mal) hay una opción que dice "generar ensamblado de serialización". Intente encenderlo para un proyecto que genere [Contiene ensamblaje de MyType] .

VladV
fuente
44
Consulte también stackoverflow.com/a/8798289/1164966 si Visual Studio aún no genera el ensamblado de serialización.
Benoit Blanchon
¡La mejor, más clara y sucinta respuesta! ¡Ojalá pudiera votar de nuevo también!
John Zabroski
59

Hay una solución para eso. Si utiliza

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

Debería evitar esa excepción. Esto funcionó para mí.

ADVERTENCIA: no lo use varias veces, o tendrá una pérdida de memoria

¡Perderá la memoria como loco si usa este método para crear instancias del XmlSerializermismo tipo más de una vez!

Esto se debe a que este método omite el almacenamiento en caché incorporado siempre que los constructores XmlSerializer(type)y XmlSerializer(type, defaultNameSpace)(todos los demás constructores también omitan el almacenamiento en caché).

Si utiliza algún método para crear un XmlSerializer que no sea a través de estos dos constructores, debe implementar su propio almacenamiento en caché o tendrá memoria de hemorragia.

quadfinity
fuente
44
ADVERTENCIA: ¡Perderá la memoria como loco si usa este método para crear instancias del XmlSerializermismo tipo más de una vez! Esto se debe a que este método omite el almacenamiento en caché incorporado siempre que los constructores XmlSerializer(type)y XmlSerializer(type, defaultNameSpace)(todos los demás constructores también omitan el almacenamiento en caché). Si usa algún método para crear uno XmlSerializerque no sea a través de estos dos constructores, debe implementar su propio almacenamiento en caché o tendrá una memoria de hemorragia.
Allon Guralnek
44
@AllonGuralnek Bueno, estaré condenado ... tienes toda la razón; profundizar más a través de Reflector muestra que si bien verifica el caché, lo hace después de generar el conjunto de serialización. Wtf?!?
JerKimball
44
Resulta que es un error conocido: weblogs.asp.net/cschittko/archive/2005/01/14/353435.aspx
JerKimball
3
@JerKimball: Esa página en realidad no está mintiendo. Como descubrió, FromTypesparece llenar el caché. Por lo tanto, debería ser una forma válida de calentar un XmlSerializercaché vacío en una declaración (como sugiere el artículo), pero una forma realmente mala de recuperar algo de él (solo debe hacerse a través de los constructores más simples). En cualquier caso, no sabía que era un error, siempre pensé que se suponía que cualquier fuga (como los XmlSerializerconstructores más avanzados ). Ni siquiera habría considerado usarlo, FromTypes()ya que puedes hacerlo types.Select(t => new XmlSerializer(t)).
Allon Guralnek
2
@AllonGuralnek El aspecto del uso que FromTypesno es de prueba tiene su atractivo: incluso si se detectan todas las excepciones, es una operación costosa; El enfoque de "almacenar en caché a su manera" parece ser la única solución, ya que la única solución oficialmente admitida parece estar en un oscuro ensamblaje basado en la web. (editar: francamente, estoy a favor de transferir todo a los contratos de datos :))
JerKimball
22

Me encontré con este problema exacto y no pude evitarlo con ninguna de las soluciones mencionadas.

Entonces finalmente encontré una solución. Parece que el serializador necesita no solo el tipo, sino también los tipos anidados. Cambiando esto:

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

A esto:

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

Solucionó el problema para mí. No más excepciones ni nada.

Escarchado
fuente
8
Esto funcionó para mí. Usando .Net4.0 el formato esvar xmlSerializer = new XmlSerializer(typeof(T), typeof(T).GetNestedTypes());
user3161729
1
Esto funcionó para mí también. Pero solo parece ser necesario cuando se serializa, no cuando se deserializa. Tal vez eso tiene sentido, tal vez no.
SteveCinq
2
Esto también produce pérdida de memoria, si se ejecuta muchas veces.
Volodymyr Kotylo
9

Mi solución es ir directamente a la reflexión para crear el serializador. Esto evita la carga extraña de archivos que causa la excepción. Empaqué esto en una función auxiliar que también se encarga de almacenar en caché el serializador.

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;
    }
}
d - b
fuente
Aquí hay dos problemas: primero, su código no es seguro para subprocesos, y segundo (más importante) está intentando replicar lo que el tiempo de ejecución de .net ya hace (según el ctor que está utilizando). es decir, no hay necesidad de este código
de Dave Negro
@DaveBlack: Sí, la respuesta de Quadfinity con el almacenamiento en caché a un ConcurrentDictionary sería mejor
d - b
@db Mi segundo punto fue que el almacenamiento en caché ni siquiera es necesario, siempre que esté usando uno de los 2 ctors que el marco almacena en caché (OP está usando el primero). Desde MSDN: para aumentar el rendimiento, la infraestructura de serialización XML genera dinámicamente ensamblajes para serializar y deserializar tipos específicos. El marco encuentra y reutiliza esos ensamblajes. Este comportamiento se produce solo cuando se utilizan los siguientes ctors: XmlSerializer.XmlSerializer (Type) XmlSerializer.XmlSerializer (Type, String) Referencia: msdn.microsoft.com/en-us/library/…
Dave Black
@DaveBlack: Sí, pero estos constructores lanzan y capturan una excepción internamente incluso cuando el uso es completamente válido. Esto es malo, y esta es la razón por la cual el OP hizo la pregunta en primer lugar.
d - b
@db Cierto, pero lo que quise decir (pero no estaba claro, mis disculpas) fue que las únicas líneas de su solución que son necesarias son las primeras 3 líneas en la condición else.
Dave Black
8

Para evitar la excepción, debe hacer dos cosas:

  1. Agregue un atributo a la clase serializada (espero que tenga acceso)
  2. Genere el archivo de serialización con sgen.exe

Agregue el atributo System.Xml.Serialization.XmlSerializerAssembly a su clase. Reemplace 'MyAssembly' con el nombre del ensamblaje donde está MyClass.

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

}

Genere el archivo de serialización utilizando la utilidad sgen.exe e impleméntelo con el ensamblado de la clase.

'sgen.exe MyAssembly.dll' generará el archivo MyAssembly.XmlSerializers.dll

Estos dos cambios harán que .net encuentre directamente el ensamblado. Lo revisé y funciona en .NET Framework 3.5 con Visual Studio 2008

Bar Ami
fuente
Ok, y falló sin estos cambios, y si es así, ¿por qué?
John Saunders, el
1
No puedo encontrar ninguna razón por la cual mi proyecto, 4.0 en VS2012, de repente comenzó a fallar. "Ignorar" el error no era una opción, porque ocurría cada vez que intentaba acceder a Active Directory; ignorarlo significaría no autenticar. Todavía estoy muy frustrado de que VS2012 no genere automáticamente la DLL de serialización correctamente. Sin embargo, estos pasos proporcionaron la solución perfecta.
sfuqua
6

Esta excepción también puede ser atrapada por un asistente de depuración administrado (MDA) llamado BindingFailure.

Este MDA es útil si su aplicación está diseñada para enviarse con ensambles de serialización precompilados. Hacemos esto para aumentar el rendimiento de nuestra aplicación. Nos permite asegurarnos de que los ensambles de serialización preconstruidos estén siendo construidos adecuadamente por nuestro proceso de construcción y cargados por la aplicación sin ser reconstruidos sobre la marcha.

Realmente no es útil, excepto en este escenario, porque como han dicho otros carteles, cuando el constructor del serializador atrapa un error vinculante, el ensamblado de serialización se reconstruye en tiempo de ejecución. Por lo tanto, generalmente puede apagarlo.

HiredMind
fuente
6

La función XmlSerializer.FromTypes no produce la excepción, pero pierde la memoria. Es por eso que necesita almacenar en caché dicho serializador para cada tipo para evitar pérdidas de memoria para cada instancia creada.

Cree su propia fábrica de XmlSerializer y úsela simplemente:

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

La fábrica parece gustos:

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

Versión más complicada sin posibilidad de pérdida de memoria (alguien revise el código):

    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;
    }       
}
Tomás Kubes
fuente
Debería usar ConcurrentDictionary en su lugar. Este código puede llegar a un punto muerto.
Behrooz el
¿Cómo puede llegar a un punto muerto si toda la administración con el diccionario está en la sección de bloqueo?
Tomas Kubes
Lo siento, tengo las palabras confundidas. Lo que quise decir es que puede insertar un elemento más de una vez. porque hay una brecha entre cuando verifica la existencia y cuando se inserta. el diccionario concurrente usa algún tipo de bloqueo de dos fases (bolsa [0] y luego bolsa [hash]]) y mantiene una referencia a la bolsa que debe insertar / contener el elemento que está trabajando. Es más rápido, más seguro y más limpio.
Behrooz el
Si y no. Tiene razón en que puede suceder que al mismo tiempo se cree un serializador del mismo tipo en dos hilos en paralelo y luego se agregue al diccionario dos veces. En tal caso, el segundo inserto solo reemplazará al primero, pero la sección de bloqueo garantiza la seguridad de la rosca y la desventaja general es una pequeña pérdida de memoria. Esta es la optimización del rendimiento, porque no desea que el subproceso uno con el serializador de tipo A espere a que el subproceso dos bloquee con el serializador de tipo B en un escenario real.
Tomas Kubes
Me imagino que la solución podría ser incluso mejor (sin pérdida de memoria teórica), pero más complicada.
Tomas Kubes
3

La resolución de errores de compilación, por otro lado, es muy complicada. Estos problemas se manifiestan en una FileNotFoundException con el mensaje:

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

Quizás se pregunte qué tiene que ver una excepción de archivo no encontrado con la creación de instancias de un objeto serializador, pero recuerde: el constructor escribe archivos C # e intenta compilarlos. La pila de llamadas de esta excepción proporciona buena información para respaldar esa sospecha. La excepción ocurrió mientras XmlSerializer intentaba cargar un ensamblado generado por CodeDOM llamando al método System.Reflection.Assembly.Load. La excepción no proporciona una explicación de por qué el ensamblado que se suponía que XmlSerializer debía crear no estaba presente. En general, el ensamblado no está presente porque la compilación falló, lo que puede suceder porque, en circunstancias excepcionales, los atributos de serialización producen código que el compilador de C # no puede compilar.

Nota: Este error también se produce cuando XmlSerializer se ejecuta en una cuenta o en un entorno de seguridad que no puede acceder al directorio temporal.

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

Zyphrax
fuente
no especificó que esto sucediera en tiempo de ejecución. Otra cosa que se me ocurre es que quizás tengas un conflicto de clase / espacio de nombres. ¿Cuál es el nombre completo de tu MyType?
Zyphrax
Sí, revisé su enlace, la información sobre los constructores, aunque útil, no era lo que necesitaba.
Irwin el
55
@SpaceghostAl Puede compilar en tiempo de ejecución. Y eso es lo que hace XmlSerializer. Construye dinámicamente en tiempo de ejecución un ensamblaje que (des) serializa XML para el tipo particular. Por alguna razón, este proceso falla para el OP. Probablemente debido a problemas de permisos, por ejemplo, en un directorio temporal. (Podría ser tan tonto como sin espacio en el disco incluso.)
nos
¿Estas seguro acerca de esto? Estaba bastante seguro de que el material de serialización se compila en un ensamblaje con el nombre YourAssemblyName.XmlSerializers.dll durante la compilación , no se compila en tiempo de ejecución. Esto podría fallar por todo tipo de razones, por lo menos de todos los permisos NTFS en la carpeta de implementación.
tomfanning
1
Desearía poder votar esto varias veces. Su nota sobre la cuenta que no puede acceder a la carpeta temporal provocó la respuesta para mí. Una vez que agregué mi cuenta de servicio al grupo de administración en el servidor, simplemente funcionó. ¡Gracias!
Bob Horn
2

En las propiedades del proyecto de Visual Studio hay una opción que dice "generar ensamblaje de serialización". Intente encenderlo para un proyecto que genere [Contiene ensamblaje de MyType].

Pascal
fuente
1

Una clase personalizada para serializar:

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

He adjuntado el fragmento de código. Tal vez esto pueda ayudarte.

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();
}
shahjapan
fuente
2
-1 por no usar bloques para evitar fugas de recursos, y por usar XmlTextWriter.
John Saunders
ok de acuerdo, pero aun así he usado XmlSerializer xmlSerializer = new XmlSerializer (typeof (TestClass)); pero no obtengo dicha Excepción.
shahjapan
1

Estaba teniendo un problema similar e ignorar la excepción no funcionó para mí. Mi código estaba llamando a la configuración de NServiceBusConfigure.With(...).XmlSerializer()...

Lo que me solucionó fue cambiar la plataforma de mi proyecto.

  1. Vaya a Build \ Configuration Manager ...
  2. Encuentre su proyecto y cambie la plataforma (en mi caso de x86 a cualquier CPU)
kkelley
fuente
1

Solo como referencia. Tomando de la respuesta y los comentarios de DB, vine con esta solución que está cerca de la solución de DB. Funciona bien en todos mis casos y es seguro para subprocesos. No creo que usar un ConcurrentDictionary hubiera estado bien.

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

Uso:

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

Su tipo puede hacer referencia a otros ensamblados que no se pueden encontrar ni en el GAC ni en su carpeta bin local ==> ...

"o una de sus dependencias. El sistema no puede encontrar el archivo especificado"

¿Puede dar un ejemplo del tipo que desea serializar?

Nota: Asegúrese de que su tipo implemente Serializable.

Henrik
fuente
0

Estaba recibiendo el mismo error, y fue debido al tipo que estaba tratando de deserializar al no tener un constructor sin parámetros predeterminado . Agregué un constructor y comenzó a funcionar.

kay.one
fuente
0

¡Tuve el mismo problema hasta que utilicé una herramienta de terceros para generar la Clase desde el XSD y funcionó! Descubrí que la herramienta estaba agregando un código extra en la parte superior de mi clase. Cuando agregué este mismo código a la parte superior de mi clase original, funcionó. Esto es lo que agregué ...

#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
  {
  ...
TheJonz
fuente
0

ConcurrentDictionaryHe visto muchas recomendaciones para usar un , pero no hay ejemplos sólidos de él, así que voy a lanzar mi sombrero en esta carrera de solución. No soy un desarrollador seguro para subprocesos, así que si este código no es sólido, hable por el bien de los que siguen.

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

He visto otras publicaciones que involucran ConcurrentDictionaryy Lazycargan el valor. No estoy seguro de si eso es relevante aquí o no, pero aquí está el código para eso:

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