La forma correcta de implementar IXmlSerializable?

Una vez que un progtwigdor decide implementar IXmlSerializable , ¿cuáles son las reglas y las mejores prácticas para implementarlo? He oído que GetSchema() debería devolver null y ReadXml debería pasar al siguiente elemento antes de volver. ¿Es esto cierto? ¿Y qué decir de WriteXml ? ¿Debería escribir un elemento raíz para el objeto o se supone que la raíz ya está escrita? ¿Cómo deben tratarse y escribirse los objetos secundarios?

Aquí hay una muestra de lo que tengo ahora. Lo actualizaré a medida que obtenga buenas respuestas.

 public class MyCalendar : IXmlSerializable { private string _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.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()); } } 

XML de muestra correspondiente

      

Sí, GetSchema () debería devolver nulo .

Método IXmlSerializable.GetSchema Este método está reservado y no debe utilizarse. Al implementar la interfaz IXmlSerializable, debe devolver una referencia nula (Nothing en Visual Basic) desde este método, y en su lugar, si se requiere especificar un esquema personalizado, aplique XmlSchemaProviderAttribute a la clase.

Tanto para lectura como para escritura, el elemento de objeto ya se ha escrito, por lo que no es necesario agregar un elemento externo en la escritura. Por ejemplo, puede comenzar a leer / escribir atributos en los dos.

Para escribir :

La implementación de WriteXml que proporcione debe escribir la representación XML del objeto. El marco escribe un elemento contenedor y posiciona el escritor XML después de su inicio. Su implementación puede escribir sus contenidos, incluidos los elementos secundarios. El marco luego cierra el elemento contenedor.

Y para leer :

El método ReadXml debe reconstituir su objeto utilizando la información escrita por el método WriteXml.

Cuando se llama a este método, el lector se coloca al comienzo del elemento que envuelve la información para su tipo. Es decir, justo antes de la etiqueta de inicio que indica el comienzo de un objeto serializado. Cuando este método retorna, debe haber leído todo el elemento de principio a fin, incluidos todos sus contenidos. A diferencia del método WriteXml, el marco no maneja el elemento contenedor automáticamente. Su implementación debe hacerlo. El incumplimiento de estas reglas de posicionamiento puede hacer que el código genere excepciones inesperadas en el tiempo de ejecución o datos corruptos.

Estoy de acuerdo en que es un poco confuso, pero se reduce a “es tu trabajo Read() la etiqueta del elemento final del envoltorio”.

Escribí un artículo sobre el tema con ejemplos, ya que la documentación de MSDN no está del todo clara y los ejemplos que puedes encontrar en la web la mayoría de las veces se implementan incorrectamente.

Las trampas son el manejo de locales y elementos vacíos al lado de lo que Marc Gravell ya mencionó.

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

Sí, todo es un campo minado, ¿no? La respuesta de Marc Gravell prácticamente lo cubre, pero me gustaría añadir que en un proyecto en el que trabajé nos resultó bastante incómodo tener que escribir manualmente el elemento XML externo. También dio como resultado nombres de elementos XML inconsistentes para objetos del mismo tipo.

Nuestra solución fue definir nuestra propia interfaz IXmlSerializable , derivada del sistema uno, que agregó un método llamado WriteOuterXml() . Como puede adivinar, este método simplemente escribiría el elemento externo, luego llamaría WriteXml() y luego escribiría el final del elemento. Por supuesto, el serializador XML del sistema no llamaría a este método, por lo que solo fue útil cuando hicimos nuestra propia serialización, por lo que puede o no ser útil en su caso. De manera similar, agregamos un método ReadContentXml() , que no leyó el elemento externo, solo su contenido.

Si ya tiene una representación XmlDocument de su clase o prefiere la forma de trabajar de XmlDocument con las estructuras XML, una manera rápida y sucia de implementar IXmlSerializable es simplemente pasar este xmldoc a las diversas funciones.

ADVERTENCIA: XmlDocument (y / o XDocument) es un orden de magnitud más lento que xmlreader / writer, por lo que si el rendimiento es un requisito absoluto, ¡esta solución no es para ti!

 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); } }