159 votes

Bonne façon d’implémentent IXmlSerializable ?

Une fois qu'un programmeur décide de mettre en œuvre IXmlSerializable, quelles sont les règles et les meilleures pratiques pour la mise en œuvre? J'ai entendu dire qu' GetSchema() doit renvoyer null et ReadXml devrait passer à l'élément suivant avant de retourner. Est-ce vrai? Et que diriez - WriteXml - faut-il écrire un élément racine de l'objet ou est-il supposé que la racine est déjà écrit? Comment doit-enfant des objets traités et à l'écrit?

Voici un échantillon de ce que j'ai maintenant. Je vais le mettre à jour que j'ai des bonnes réponses.

public class MyCalendar : IXmlSerializable
{
    private string _name;
    private bool _enabled;
    private Color _color;
    private List<MyEvent> _events = new List<MyEvent>();


    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.WriteAttributeString("Name",    _name);
        writer.WriteAttributeString("Enabled", _enabled.ToString());
        writer.WriteAttributeString("Color",   _color.ToArgb().ToString());

        foreach (MyEvent evt in _events)
        {
            writer.WriteStartElement("MyEvent");
            evt.WriteXml(writer);
            writer.WriteEndElement();
        }
    }
}

public class MyEvent : IXmlSerializable
{
    private string _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.WriteAttributeString("Title", _title);
        writer.WriteAttributeString("Start", _start.ToBinary().ToString());
        writer.WriteAttributeString("Stop",  _stop.ToBinary().ToString());
    }
}

Correspondant de l'Échantillon XML

<MyCalendar Name="Master Plan" Enabled="True" Color="-14069085">
    <MyEvent Title="Write Code" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="???" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="Profit!" Start="-8589247048854775808" Stop="-8589246976854775808" />
</MyCalendar>

106voto

Marc Gravell Points 482669

Oui, GetSchema() doit retourner la valeur null.

IXmlSerializable.GetSchema Cette Méthode la méthode est réservé et ne doit pas être utilisé. Lors de la mise en œuvre de la IXmlSerializable interface, vous devez de retour d'une référence null (Nothing en Visual Basic) à partir de cette méthode, et au lieu de cela, si la spécification d'un schéma personnalisé est nécessaire, appliquer le XmlSchemaProviderAttribute à l' classe.

Pour lire et écrire, l'objet de l'élément a déjà été écrit, de sorte que vous n'avez pas besoin d'ajouter un élément externe à écrire. Par exemple, vous pouvez commencer la lecture/écriture des attributs dans les deux.

Pour écrire:

Le WriteXml mise en œuvre vous fournir doivent écrire du XML la représentation de l'objet. L' cadre écrit un élément wrapper et les positions de l'XML écrivain après sa commencer. Votre application peut écrire son contenu, y compris les enfants éléments. Le cadre, puis ferme l'élément wrapper.

Et pour lire:

La méthode ReadXml doit reconstituer votre objet à l'aide de l'information que a été écrit par la méthode WriteXml.

Lorsque cette méthode est appelée, le lecteur est positionné au début de la élément qui encapsule les informations pour votre type. C'est, juste avant le début de la balise qui indique le début d'un objet sérialisé. Lorsque cette méthode retourne, il doit avoir lu les l'élément complet du début à la fin, y compris l'ensemble de son contenu. Contrairement à la méthode WriteXml, le cadre ne pas manipuler l'élément wrapper automatiquement. Votre mise en œuvre doit le faire. Non-respect de ces le positionnement des règles peut entraîner code de générer inattendue des exceptions d'exécution ou de corrompre les données.

Je suis d'accord que c'est un peu flou, mais ça se résume à "c'est votre travail pour Read() à la fin de balise de l'élément de l'emballage".

35voto

jdehaan Points 14019

J’ai écrit un article sur le sujet avec des échantillons que la documentation MSDN est maintenant assez peu claires et les exemples que vous pouvez trouver sur le web sont la plupart du temps pas correctement mis en œuvre.

Pièges sont manipulation des paramètres régionaux et les éléments vides à côté de ce que Marc Gravell a déjà mentionné.

http://www.CodeProject.com/kb/XML/ImplementIXmlSerializable.aspx

9voto

EMP Points 17246

Oui, le tout est un peu un champ de mines, n'est-ce pas? Marc Gravel's réponse couvre assez bien, mais je tiens à ajouter que dans un projet, j'ai travaillé sur nous avons trouvé il assez inconfortable d'avoir à écrire manuellement l'extérieur de l'élément XML. Il a également entraîné des incohérences dans l'élément XML de noms pour les objets de même type.

Notre solution a été de définir notre propre IXmlSerializable interface, dérivé de celui du système, qui a ajouté une méthode appelée WriteOuterXml(). Comme vous pouvez le deviner, cette méthode pourrait simplement écrire l'élément extérieur, puis appelez WriteXml(), puis écrire la fin de l'élément. Bien sûr, le système de sérialiseur XML ne pas appeler cette méthode, il était donc utile lorsque nous avons fait notre propre sérialisation, de sorte que peut ou peut ne pas être utile dans votre cas. De la même façon, nous avons ajouté un ReadContentXml() méthode, qui n'a pas lu l'élément extérieur, seulement son contenu.

2voto

Bob Verkouteren Points 36

Si vous déjà avez une représentation XmlDocument de votre classe ou préférez la manière XmlDocument de travailler avec des structures XML, un moyen rapide et sale d’implémentation IXmlSerializable est juste passer cette xmldoc aux différentes fonctions.

AVERTISSEMENT : XmlDocument (et/ou XDocument) est un ordre de grandeur plus lente que xmlreader/écrivain, alors si les performances constituent une exigence absolue, cette solution n’est pas pour vous !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X