Una vez que un programador decide implementar IXmlSerializable
, ¿cuáles son las reglas y las mejores prácticas para implementarlo? Escuché que GetSchema()
debería regresar null
y ReadXml
debería pasar al siguiente elemento antes de regresar. ¿Es esto cierto? ¿Y qué pasa si WriteXml
debería escribir un elemento raíz para el objeto o se supone que la raíz ya está escrita? ¿Cómo se deben tratar y escribir 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<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());
}
}
XML de muestra correspondiente
<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
fuente
fuente
Respuestas:
Sí, GetSchema () debería devolver nulo .
Tanto para lectura como para escritura, el elemento objeto ya se ha escrito, por lo que no necesita agregar un elemento externo en escritura. Por ejemplo, puede comenzar a leer / escribir atributos en los dos.
Para escribir :
Y para leer :
Estoy de acuerdo en que no está claro, pero se reduce a "es tu trabajo
Read()
la etiqueta del elemento final del contenedor".fuente
Escribí un artículo sobre el tema con ejemplos, ya que la documentación de MSDN no está clara y los ejemplos que puede encontrar en la web se implementan incorrectamente la mayoría de las veces.
Las trampas son el manejo de locales y elementos vacíos junto a lo que Marc Gravell ya mencionó.
http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx
fuente
Sí, todo es un campo minado, ¿no? La respuesta de Marc Gravell lo cubre más o menos, pero me gustaría agregar que en un proyecto en el que trabajé nos pareció bastante incómodo tener que escribir manualmente el elemento XML externo. También resultó en nombres de elementos XML inconsistentes para objetos del mismo tipo.
Nuestra solución fue definir nuestra propia
IXmlSerializable
interfaz, derivada de la del sistema, que agregó un método llamadoWriteOuterXml()
. Como puede adivinar, este método simplemente escribiría el elemento externo, luego llamaríaWriteXml()
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. Del mismo modo, agregamos unReadContentXml()
método, que no leía el elemento externo, solo su contenido.fuente
Si ya tiene una representación XmlDocument de su clase o prefiere la forma XmlDocument de trabajar con estructuras XML, una forma 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 usted!
fuente
La implementación de la interfaz está cubierta por las otras respuestas, pero quería agregar mis 2 centavos para el elemento raíz.
En el pasado aprendí a preferir colocar el elemento raíz como metadatos. Esto tiene algunos beneficios:
A continuación se muestra un ejemplo de un diccionario serializable donde el elemento raíz del diccionario se define de esa manera:
fuente