Manière correcte d’implémenter IXmlSerializable?

Une fois qu’un programmeur décide d’implémenter IXmlSerializable , quelles sont les règles et les meilleures pratiques pour l’implémenter? J’ai entendu dire que GetSchema() devrait renvoyer null et que ReadXml devrait passer à l’élément suivant avant de retourner. Est-ce vrai? Et qu’en est-il de WriteXml – devrait-il écrire un élément racine pour l’object ou est-il supposé que la racine est déjà écrite? Comment les objects enfants doivent-ils être traités et écrits?

Voici un exemple de ce que j’ai maintenant. Je vais le mettre à jour au fur et à mesure que j’obtiens de bonnes réponses.

 public class MyCalendar : IXmlSerializable { private ssortingng _name; private bool _enabled; private Color _color; private List _events = new List(); public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCalendar") { _name = reader["Name"]; _enabled = Boolean.Parse(reader["Enabled"]); _color = Color.FromArgb(Int32.Parse(reader["Color"])); if (reader.ReadToDescendant("MyEvent")) { while (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent") { MyEvent evt = new MyEvent(); evt.ReadXml(reader); _events.Add(evt); } } reader.Read(); } } public void WriteXml(XmlWriter writer) { writer.WriteAtsortingbuteSsortingng("Name", _name); writer.WriteAtsortingbuteSsortingng("Enabled", _enabled.ToSsortingng()); writer.WriteAtsortingbuteSsortingng("Color", _color.ToArgb().ToSsortingng()); foreach (MyEvent evt in _events) { writer.WriteStartElement("MyEvent"); evt.WriteXml(writer); writer.WriteEndElement(); } } } public class MyEvent : IXmlSerializable { private ssortingng _title; private DateTime _start; private DateTime _stop; public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent") { _title = reader["Title"]; _start = DateTime.FromBinary(Int64.Parse(reader["Start"])); _stop = DateTime.FromBinary(Int64.Parse(reader["Stop"])); reader.Read(); } } public void WriteXml(XmlWriter writer) { writer.WriteAtsortingbuteSsortingng("Title", _title); writer.WriteAtsortingbuteSsortingng("Start", _start.ToBinary().ToSsortingng()); writer.WriteAtsortingbuteSsortingng("Stop", _stop.ToBinary().ToSsortingng()); } } 

Exemple XML correspondant

      

Oui, GetSchema () doit retourner null .

Méthode IXmlSerializable.GetSchema Cette méthode est réservée et ne doit pas être utilisée. Lors de l’implémentation de l’interface IXmlSerializable, vous devez renvoyer une référence null (Nothing en Visual Basic) à partir de cette méthode et, si vous devez spécifier un schéma personnalisé, appliquez le XmlSchemaProviderAtsortingbute à la classe.

Pour lire et écrire, l’élément object a déjà été écrit, vous n’avez donc pas besoin d’append un élément externe en écriture. Par exemple, vous pouvez simplement commencer à lire / écrire des atsortingbuts dans les deux.

Pour écrire :

L’implémentation WriteXml que vous fournissez doit écrire la représentation XML de l’object. Le framework écrit un élément wrapper et positionne le scripteur XML après son démarrage. Votre implémentation peut écrire son contenu, y compris des éléments enfants. Le framework ferme alors l’élément wrapper.

Et pour lire :

La méthode ReadXml doit reconstituer votre object en utilisant les informations écrites par la méthode WriteXml.

Lorsque cette méthode est appelée, le lecteur est positionné au début de l’élément qui encapsule les informations pour votre type. C’est-à-dire juste avant la balise de début qui indique le début d’un object sérialisé. Lorsque cette méthode retourne, elle doit avoir lu l’intégralité de l’élément du début à la fin, y compris tout son contenu. Contrairement à la méthode WriteXml, la structure ne gère pas automatiquement l’élément wrapper. Votre implémentation doit le faire. Si vous ne respectez pas ces règles de positionnement, le code risque de générer des exceptions d’exécution inattendues ou des données corrompues.

Je suis d’accord que c’est un peu flou, mais ça se résume à “c’est votre travail de Read() la balise de l’élément final du wrapper”.

J’ai écrit un article sur le sujet avec des exemples, car la documentation MSDN est désormais peu claire et les exemples que vous pouvez trouver sur le Web sont la plupart du temps mal implémentés.

Les pièges sont la manipulation des lieux et des éléments vides à côté de ce que Marc Gravell a déjà mentionné.

http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx

Oui, le tout est un peu un champ de mines, n’est-ce pas? La réponse de Marc Gravell la couvre à peu près, mais j’aimerais append que dans un projet sur lequel j’ai travaillé, nous avons trouvé assez difficile de devoir écrire manuellement l’élément XML externe. Il en résultait également des noms d’éléments XML incohérents pour les objects du même type.

Notre solution consistait à définir notre propre interface IXmlSerializable , dérivée de celle du système, qui ajoutait une méthode appelée WriteOuterXml() . Comme vous pouvez le deviner, cette méthode consisterait simplement à écrire l’élément externe, puis à écrire WriteXml() , puis à écrire la fin de l’élément. Bien entendu, le sérialiseur XML du système n’appellerait pas cette méthode, elle n’était donc utile que lorsque nous effectuions notre propre sérialisation, ce qui peut ne pas être utile dans votre cas. De même, nous avons ajouté une méthode ReadContentXml() , qui ne lisait pas l’élément externe, mais uniquement son contenu.

Si vous avez déjà une représentation XmlDocument de votre classe ou préférez la méthode XmlDocument pour travailler avec les structures XML, une manière rapide et sale d’implémenter IXmlSerializable consiste à simplement passer ce xmldoc aux différentes fonctions.

AVERTISSEMENT: XmlDocument (et / ou XDocument) est un ordre de grandeur plus lent que xmlreader / writer, donc si la performance est une exigence absolue, cette solution n’est pas pour vous!

 class ExampleBaseClass : IXmlSerializable { public XmlDocument xmlDocument { get; set; } public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { xmlDocument.Load(reader); } public void WriteXml(XmlWriter writer) { xmlDocument.WriteTo(writer); } }