J'écris du code pour faire la sérialisation Xml. Avec la fonction ci-dessous.
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 l'argument est une instance de classe sans constructeur sans paramètre, il lèvera une exception.
Exception non gérée: System.InvalidOperationException: CSharpConsole.Foo ne peut pas être sérialisé car il n'a pas de constructeur sans paramètre. à System.Xml.Serialization.TypeDesc.CheckSupported () à System.Xml.Serialization.TypeScope.GetTypeDesc (Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) à System.Xml.Serialization.ModelScope.GetTypeModel (Type type, Boolean direct Reference) à System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (Type type, racine XmlRootAttribute, String defaultNamespace) à System.Xml.Serialization.XmlSerializer..ctor (Type type, String defaultName space) à System.Xml.Serialization. XmlSerializer..ctor (type de type)
Pourquoi doit-il y avoir un constructeur sans paramètre pour permettre à la sérialisation XML de réussir?
EDIT: merci pour la réponse de cfeduke. Le constructeur sans paramètre peut être privé ou interne.
la source
XmlSerializer
nécessite un constructeur sans paramètre par défaut pour la désérialisation.Réponses:
Lors de la désérialisation d'un objet, la classe responsable de la désérialisation d'un objet crée une instance de la classe sérialisée, puis procède au remplissage des champs et propriétés sérialisés uniquement après l'acquisition d'une instance à remplir.
Vous pouvez créer votre constructeur
private
ouinternal
si vous le souhaitez, du moment qu'il est sans paramètre.la source
private
ouinternal
, toutes vos propriétés dont les valeurs ont été sérialisées doivent avoir despublic
setters.Ceci est une limitation de
XmlSerializer
. Notez queBinaryFormatter
etDataContractSerializer
ne l' exigent pas - ils peuvent créer un objet non initialisé à partir de l'éther et l'initialiser pendant la désérialisation.Puisque vous utilisez xml, vous pouvez envisager d'utiliser
DataContractSerializer
et de marquer votre classe avec[DataContract]
/[DataMember
], mais notez que cela change le schéma (par exemple, il n'y a pas d'équivalent de[XmlAttribute]
- tout devient des éléments).Mise à jour: si vous voulez vraiment savoir,
BinaryFormatter
et al utilisentFormatterServices.GetUninitializedObject()
pour créer l'objet sans appeler le constructeur. Probablement dangereux; Je ne recommande pas de l'utiliser trop souvent ;-p Voir aussi les remarques sur MSDN:J'ai mon propre moteur de sérialisation, mais je n'ai pas l'intention de l'utiliser
FormatterServices
; J'aime bien savoir qu'un constructeur ( n'importe quel constructeur) s'est réellement exécuté.la source
FormatterServices
utilisation pour les âgesIXmlSerializable
, mais a: qui se produit après le constructeur, et b: c'est très moche et difficile à faire (surtout la désérialisation) - je déconseille fortement d'essayer de l'implémenter, mais: cela ne vous permettra pas d'utiliser des constructeursLa réponse est: sans aucune raison valable.
Contrairement à son nom, la
XmlSerializer
classe est utilisée non seulement pour la sérialisation, mais aussi pour la désérialisation. Il effectue certains contrôles sur votre classe pour s'assurer que cela fonctionnera, et certains de ces contrôles ne sont pertinents que pour la désérialisation, mais il les effectue tous de toute façon, car il ne sait pas ce que vous comptez faire plus tard.La vérification que votre classe ne réussit pas est l'une des vérifications qui ne concernent que la désérialisation. Voici ce qui se passe:
Lors de la désérialisation, la
XmlSerializer
classe devra créer des instances de votre type.Afin de créer une instance d'un type, un constructeur de ce type doit être appelé.
Si vous n'avez pas déclaré de constructeur, le compilateur a déjà fourni un constructeur sans paramètre par défaut, mais si vous avez déclaré un constructeur, alors c'est le seul constructeur disponible.
Donc, si le constructeur que vous avez déclaré accepte des paramètres, la seule façon d'instancier votre classe est d'appeler ce constructeur qui accepte les paramètres.
Cependant,
XmlSerializer
n'est pas capable d'appeler un constructeur sauf un constructeur sans paramètre, car il ne sait pas quels paramètres passer aux constructeurs qui acceptent des paramètres. Donc, il vérifie si votre classe a un constructeur sans paramètre, et comme ce n'est pas le cas, il échoue.Donc, si la
XmlSerializer
classe avait été écrite de manière à n'effectuer que les vérifications pertinentes pour la sérialisation, alors votre classe passerait, car il n'y a absolument rien dans la sérialisation qui rend nécessaire d'avoir un constructeur sans paramètre.Comme d'autres l'ont déjà souligné, la solution rapide à votre problème consiste simplement à ajouter un constructeur sans paramètre. Malheureusement, c'est aussi une solution sale, car cela signifie que vous ne pouvez pas avoir de
readonly
membres initialisés à partir des paramètres du constructeur.En plus de tout cela, la
XmlSerializer
classe aurait pu être écrite de manière à permettre même la désérialisation des classes sans constructeurs sans paramètre. Tout ce qu'il faudrait serait d'utiliser "The Factory Method Design Pattern" (Wikipedia) . À première vue, Microsoft a décidé que ce modèle de conception était beaucoup trop avancé pour les programmeurs DotNet, qui ne devraient apparemment pas être confondus inutilement avec de telles choses. Ainsi, les programmeurs DotNet devraient mieux s'en tenir aux constructeurs sans paramètre, selon Microsoft.la source
For no good reason whatsoever,
puis continuez en disant,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.
s'il ne sait pas quels paramètres passer à un constructeur, alors comment saurait-il quels paramètres passer à une usine? Ou quelle usine utiliser? Je ne peux pas imaginer que cet outil soit plus simple à utiliser - vous voulez une classe désérialisée, puis laissez le désérialiseur créer une instance par défaut, puis remplissez chaque champ que vous avez balisé. Facile.Tout d'abord, c'est ce qui est écrit dans la documentation . Je pense que c'est l'un de vos champs de classe, pas le principal - et comment voulez-vous que le désérialiseur le reconstruise sans construction sans paramètre?
Je pense qu'il existe une solution de contournement pour rendre le constructeur privé.
la source