Une fois qu'un programmeur décide de l'implémenter IXmlSerializable
, quelles sont les règles et les meilleures pratiques pour l'implémenter? J'ai entendu dire que cela GetSchema()
devrait revenir null
et ReadXml
passer à l'élément suivant avant de revenir. Est-ce vrai? Et qu'en WriteXml
est-il - doit-il écrire un élément racine pour l'objet ou est-il supposé que la racine est déjà écrite? Comment les objets enfants doivent-ils être traités et écrits?
Voici un échantillon 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 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());
}
}
Échantillon XML correspondant
<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>
c#
xml
xml-serialization
Greg
la source
la source
Réponses:
Oui, GetSchema () doit retourner null .
Pour la lecture et l'écriture, l'élément objet a déjà été écrit, vous n'avez donc pas besoin d'ajouter un élément externe en écriture. Par exemple, vous pouvez simplement commencer à lire / écrire des attributs dans les deux.
Pour écrire :
Et pour lire :
Je conviens que ce n'est pas clair, mais cela se résume à "c'est votre travail à
Read()
la balise d'élément de fin de l'emballage".la source
J'ai écrit un article sur le sujet avec des exemples car la documentation MSDN est maintenant assez floue et les exemples que vous pouvez trouver sur le Web sont la plupart du temps mal implémentés.
Les pièges sont la gestion des locales et des éléments vides à côté de ce que Marc Gravell a déjà mentionné.
http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx
la source
Oui, le tout est un peu un champ de mines, n'est-ce pas? La réponse de Marc Gravell le couvre à peu près, mais j'aimerais ajouter que dans un projet sur lequel j'ai travaillé, nous avons trouvé assez gênant d'avoir à écrire manuellement l'élément XML externe. Cela entraînait également des noms d'éléments XML incohérents pour les objets du même type.
Notre solution a été de définir notre propre
IXmlSerializable
interface, dérivée de celle du système, qui a ajouté une méthode appeléeWriteOuterXml()
. Comme vous pouvez le deviner, cette méthode écrirait simplement l'élément extérieur, puis appelleraitWriteXml()
, puis écrirait la fin de l'élément. Bien sûr, le sérialiseur XML système n'appellerait pas cette méthode, donc cela n'était utile que lorsque nous faisions notre propre sérialisation, donc cela peut ou non être utile dans votre cas. De même, nous avons ajouté uneReadContentXml()
méthode, qui n'a pas lu l'élément externe, uniquement son contenu.la source
Si vous avez déjà une représentation XmlDocument de votre classe ou préférez la manière XmlDocument de travailler avec des structures XML, une manière rapide et sale d'implémenter IXmlSerializable consiste simplement à transmettre ce xmldoc aux différentes fonctions.
AVERTISSEMENT: XmlDocument (et / ou XDocument) est un ordre de grandeur plus lent que xmlreader / writer, donc si les performances sont une exigence absolue, cette solution n'est pas pour vous!
la source
L'implémentation de l'interface est couverte par les autres réponses, mais je voulais ajouter mes 2 cents pour l'élément racine.
J'ai appris dans le passé à préférer mettre l'élément racine en tant que métadonnées. Cela présente quelques avantages:
Voici un exemple de dictionnaire sérialisable où l'élément racine du dictionnaire est défini de cette manière:
la source