一尘不染

实现IXmlSerializable的正确方法?

c#

一旦程序员决定实施IXmlSerializable,实施的规则和最佳实践是什么?我听说GetSchema()应该返回null并且ReadXml应该在返回之前移至下一个元素。这是真的?怎么样WriteXml-它应该为对象写一个根元素,还是假定该根已经被写入?应该如何对待和书写子物件?

这是我现在所拥有的样本。我会在收到良好回应后对其进行更新。

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

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

阅读 869

收藏
2020-05-19

共1个答案

一尘不染

是的,GetSchema()应该返回null

IXmlSerializable.GetSchema方法此方法是保留的,不应使用。在实现IXmlSerializable接口时,应从此方法返回一个空引用(在Visual
Basic中为Nothing),并且,如果需要指定自定义架构,则将XmlSchemaProviderAttribute应用于该类。

对于读写,object元素已经被写入,因此您无需在write中添加外部元素。例如,您可以只开始读取/写入两者中的属性。

对于

您提供的WriteXml实现应写出对象的XML表示形式。该框架将编写包装器元素,并在XML编写器启动后对其进行定位。您的实现可以编写其内容,包括子元素。然后,框架关闭包装器元素。

而对于阅读

ReadXml方法必须使用WriteXml方法编写的信息来重构您的对象。

调用此方法时,阅读器位于包装您的类型信息的元素的开头。也就是说,就在指示序列化对象开始的开始标记之前。当此方法返回时,它必须从头到尾读取整个元素,包括其所有内容。与WriteXml方法不同,该框架不会自动处理wrapper元素。您的实现必须这样做。不遵守这些定位规则可能会导致代码生成意外的运行时异常或损坏的数据。

我会同意这一点还不清楚,但是归结为“这是Read()包装器末端元素标记的工作”。

2020-05-19