c# - serialize - Maneira correta de implementar o IXmlSerializable?




save object in xml c# (3)

Quando um programador decide implementar o IXmlSerializable , quais são as regras e melhores práticas para implementá-lo? Ouvi dizer que GetSchema() deve retornar null e ReadXml deve passar para o próximo elemento antes de retornar. Isso é verdade? E sobre o WriteXml - ele deve escrever um elemento raiz para o objeto ou é assumido que a raiz já está escrita? Como os objetos filhos devem ser tratados e escritos?

Aqui está uma amostra do que tenho agora. Vou atualizá-lo enquanto recebo boas respostas.

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 amostra correspondente

<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>

Eu escrevi um artigo sobre o assunto com amostras, pois a documentação do MSDN ainda não está clara e os exemplos que você pode encontrar na web são, na maioria das vezes, implementados incorretamente.

Armadilhas são manipulação de locais e elementos vazios ao lado do que Marc Gravell já mencionou.

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


Se você já tem uma representação XmlDocument de sua classe ou prefere a maneira XmlDocument de trabalhar com estruturas XML, uma maneira rápida e suja de implementar IXmlSerializable é apenas passar este xmldoc para as várias funções.

AVISO: XmlDocument (e / ou XDocument) é uma ordem de magnitude mais lenta que xmlreader / writer, portanto, se o desempenho for um requisito absoluto, esta solução não é para você!

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

Sim, a coisa toda é um pouco minada, não é? A resposta de Marc Gravell praticamente cobre isso, mas eu gostaria de acrescentar que em um projeto em que trabalhei achamos bastante complicado ter que escrever manualmente o elemento XML externo. Isso também resultou em nomes de elementos XML inconsistentes para objetos do mesmo tipo.

Nossa solução foi definir nossa própria interface IXmlSerializable , derivada do sistema one, que adicionava um método chamado WriteOuterXml() . Como você pode imaginar, este método simplesmente escreveria o elemento externo, então chamaria WriteXml() , e então gravaria o final do elemento. É claro que o serializador XML do sistema não chamaria esse método, portanto, isso só era útil quando fizemos nossa própria serialização, de modo que pode ou não ser útil no seu caso. Da mesma forma, adicionamos um método ReadContentXml() , que não leu o elemento externo, apenas seu conteúdo.





xml-serialization