Estoy escribiendo código para hacer la serialización Xml. Con la siguiente función.
public static string SerializeToXml(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
}
Si el argumento es una instancia de clase sin un constructor sin parámetros, arrojará una excepción.
Excepción no controlada: System.InvalidOperationException: CSharpConsole.Foo no se puede serializar porque no tiene un constructor sin parámetros. en System.Xml.Serialization.TypeDesc.CheckSupported () en System.Xml.Serialization.TypeScope.GetTypeDesc (Tipo de tipo, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) en System.Xml.Serialization.ModelScope.GetTypeModel (Type type, modelo Referencia directa booleana) en System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (Type type, XmlRootAttribute root, String defaultNamespace) en System.Xml.Serialization.XmlSerializer..ctor (Type type, String defaultName space) en System.Xml.Serialization. XmlSerializer..ctor (Tipo de tipo)
¿Por qué debe haber un constructor sin parámetros para permitir que la serialización xml tenga éxito?
EDITAR: gracias por la respuesta de cfeduke. El constructor sin parámetros puede ser privado o interno.
fuente
XmlSerializer
requiere un constructor sin parámetros predeterminado para la deserialización.Respuestas:
Durante la deserialización de un objeto, la clase responsable de deserializar un objeto crea una instancia de la clase serializada y luego completa los campos y propiedades serializados solo después de adquirir una instancia para rellenar.
Puede hacer su constructor
private
ointernal
si lo desea, siempre que no tenga parámetros.fuente
private
ointernal
, todas sus propiedades cuyos valores se serializaron deben tenerpublic
definidores.Esta es una limitación de
XmlSerializer
. Tenga en cuenta queBinaryFormatter
yDataContractSerializer
no lo requiere: pueden crear un objeto no inicializado del éter e inicializarlo durante la deserialización.Como está utilizando xml, puede considerar usar
DataContractSerializer
y marcar su clase con[DataContract]
/[DataMember
], pero tenga en cuenta que esto cambia el esquema (por ejemplo, no hay equivalente de[XmlAttribute]
- todo se convierte en elementos).Actualización: si realmente quieres saber,
BinaryFormatter
et al. UsaFormatterServices.GetUninitializedObject()
para crear el objeto sin invocar al constructor. Probablemente peligroso; No recomiendo usarlo con demasiada frecuencia ;-p Vea también los comentarios en MSDN:Tengo mi propio motor de serialización, pero no tengo la intención de utilizarlo
FormatterServices
; Me gusta mucho saber que un constructor ( cualquier constructor) realmente se ha ejecutado.fuente
FormatterServices
uso por edadesIXmlSerializable
sino una: lo que sucede después de la constructora, y b: es muy desagradable y difícil de conseguir a la derecha (en especial deserialización) - Yo recomiendo no tratar de implementar, pero: no se permitirá utilizar constructoresLa respuesta es: sin ninguna buena razón.
Contrariamente a su nombre, la
XmlSerializer
clase se usa no solo para la serialización, sino también para la deserialización. Realiza ciertas comprobaciones en su clase para asegurarse de que funcionará, y algunas de esas comprobaciones solo son pertinentes para la deserialización, pero las realiza todas de todos modos, porque no sabe qué piensa hacer más adelante.El control que su clase no pasa es uno de los controles que solo son pertinentes para la deserialización. Esto es lo que pasa:
Durante la deserialización, la
XmlSerializer
clase necesitará crear instancias de su tipo.Para crear una instancia de un tipo, se debe invocar un constructor de ese tipo.
Si no declaró un constructor, el compilador ya ha proporcionado un constructor sin parámetros predeterminado, pero si declaró un constructor, entonces ese es el único constructor disponible.
Entonces, si el constructor que declaró acepta parámetros, entonces la única forma de instanciar su clase es invocando ese constructor que acepta parámetros.
Sin embargo,
XmlSerializer
no es capaz de invocar ningún constructor, excepto un constructor sin parámetros, porque no sabe qué parámetros pasar a los constructores que aceptan parámetros. Por lo tanto, verifica si su clase tiene un constructor sin parámetros y, como no lo tiene, falla.Entonces, si la
XmlSerializer
clase se hubiera escrito de tal manera que solo realizara las verificaciones pertinentes a la serialización, entonces su clase pasaría, porque no hay absolutamente nada sobre la serialización que haga necesario tener un constructor sin parámetros.Como otros ya han señalado, la solución rápida a su problema es simplemente agregar un constructor sin parámetros. Desafortunadamente, también es una solución sucia, porque significa que no puede tener ningún
readonly
miembro inicializado a partir de los parámetros del constructor.Además de todo esto, la
XmlSerializer
clase podría haberse escrito de tal manera que permita incluso la deserialización de clases sin constructores sin parámetros. Todo lo que se necesitaría sería hacer uso del "Patrón de diseño del método de fábrica" (Wikipedia) . Por lo que parece, Microsoft decidió que este patrón de diseño es demasiado avanzado para los programadores de DotNet, que aparentemente no deberían confundirse innecesariamente con tales cosas. Entonces, los programadores de DotNet deberían adherirse mejor a los constructores sin parámetros, según Microsoft.fuente
For no good reason whatsoever,
luego continúa,XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters.
si no sabe qué parámetros pasarle a un constructor, ¿cómo sabría qué parámetros pasarle a una fábrica? ¿O qué fábrica usar? No puedo imaginar que esta herramienta sea más fácil de usar: desea una clase deserializada, luego deje que el deserializador haga una instancia predeterminada y luego complete cada campo que etiquetó. Fácil.En primer lugar, esto es lo que está escrito en la documentación . Creo que es uno de los campos de su clase, no el principal, y ¿cómo desea que el deserialiser lo vuelva a construir sin construcción sin parámetros?
Creo que hay una solución alternativa para hacer que el constructor sea privado.
fuente