C# 实现IXmlSerializable的正确方法?

C# 实现IXmlSerializable的正确方法?,c#,xml,xml-serialization,C#,Xml,Xml Serialization,一旦程序员决定实现IXmlSerializable,实现它的规则和最佳实践是什么?我听说GetSchema()应该返回null,ReadXml应该在返回前移动到下一个元素。这是真的吗?那么关于WriteXml——它应该为对象写一个根元素,还是假设根元素已经被写了?如何处理和写入子对象 这是我现在拥有的一个样本。我会在收到好的回复后更新它 public class MyCalendar : IXmlSerializable { private string _name; priva

一旦程序员决定实现
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());
    }
}
公共类MyCalendar:IXmlSerializable { 私有字符串\u名称; 启用私有布尔; 私人色彩(u色),; 私有列表_事件=新列表(); 公共XmlSchema GetSchema(){return null;} 公共void ReadXml(XmlReader) { if(reader.MoveToContent()==XmlNodeType.Element&&reader.LocalName==“MyCalendar”) { _名称=读卡器[“名称”]; _enabled=Boolean.Parse(读取器[“enabled”]); _color=color.FromArgb(Int32.Parse(reader[“color”]); if(reader.ReadToDescendant(“MyEvent”)) { while(reader.MoveToContent()==XmlNodeType.Element&&reader.LocalName==“MyEvent”) { MyEvent evt=新的MyEvent(); ReadXml(reader); _添加事件(evt); } } reader.Read(); } } public void WriteXml(XmlWriter) { WriteAttributeString(“Name”,_Name); WriteAttributeString(“Enabled”,_Enabled.ToString()); WriteAttributeString(“Color”,_Color.ToArgb().ToString()); foreach(MyEvent evt in_事件) { WriteStarteElement(“MyEvent”); evt.WriteXml(编写器); writer.writeedelement(); } } } 公共类MyEvent:IXmlSerializable { 私有字符串\u标题; 私人日期时间(u start);; 私人日期时间站; 公共XmlSchema GetSchema(){return null;} 公共void ReadXml(XmlReader) { if(reader.MoveToContent()==XmlNodeType.Element&&reader.LocalName==“MyEvent”) { _标题=读者[“标题”]; _start=DateTime.FromBinary(Int64.Parse(reader[“start”]); _stop=DateTime.FromBinary(Int64.Parse(reader[“stop”]); reader.Read(); } } public void WriteXml(XmlWriter) { WriteAttributeString(“Title”,_Title); WriteAttributeString(“Start”,_Start.ToBinary().ToString()); 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>

是,GetSchema()

IXmlSerializable.GetSchema此方法 方法是保留的,不应 用过。在实施 IXmlSerializable接口,您应该 返回一个空引用(在 VisualBasic),而不是, 如果无法指定自定义架构 如果需要,请应用 XmlSchemaProviderAttribute到 班级

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

用于:

您需要的WriteXml实现 provide应该写出XML 对象的表示。这个 框架编写一个包装器元素并 将XML编写器放置在其 开始您的实现可以编写 它的内容,包括子 元素。然后框架关闭 包装器元素

以及:

ReadXml方法必须重新构造 使用以下信息创建对象: 是通过WriteXml方法编写的

调用此方法时,读取器 位于 元素,该元素为 你喜欢的类型。也就是说,就在 指示开始的开始标记 序列化对象的。当这 方法返回时,它必须已读取 从开始到结束的整个元素, 包括它的全部内容。不像 WriteXml方法,框架 不处理包装器元素 自动地您的实现 我们必须这样做。没有遵守这些规定 定位规则可能导致代码丢失 生成意外的运行时异常 或损坏数据


我同意这有点不清楚,但归根结底是“你的工作就是阅读包装的结束元素标签。”

我写了一篇关于这个主题的文章,其中有一些例子,因为MSDN文档现在还不清楚,你在web上可以找到的例子大部分时间都没有正确实现

除了Marc Gravell已经提到的内容外,陷阱在于处理区域设置和空元素


是的,整个事情有点像雷区,不是吗Marc Gravell的回答几乎涵盖了这一点,但我想补充一点,在我参与的一个项目中,我们发现手动编写外部XML元素非常困难。它还导致相同类型的对象的XML元素名称不一致


我们的解决方案是定义我们自己的
IXmlSerializable
接口,该接口源自system one,它添加了一个名为
WriteOuterXml()
的方法。正如您所猜测的,此方法只需编写外部元素,然后调用
WriteXml()
,然后编写元素的结尾。当然,系统XML序列化程序不会调用此方法,因此它只有在我们自己进行序列化时才有用,因此在您的情况下可能有用,也可能无用。类似地,我们添加了一个
ReadContentXml()
方法,该方法不读取外部元素,只读取其内容。

如果您已经拥有类的XmlDocument表示形式,或者更喜欢使用XMLStru的XmlDocument方式
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);
    }
}
using System.Collections.Generic;

[System.Xml.Serialization.XmlRoot("dictionary")]
public partial class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, System.Xml.Serialization.IXmlSerializable
{
            public virtual System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public virtual void ReadXml(System.Xml.XmlReader reader)
    {
        var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
        var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();
        if (wasEmpty)
            return;
        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");
            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
            Add(key, value);
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        reader.ReadEndElement();
    }

    public virtual void WriteXml(System.Xml.XmlWriter writer)
    {
        var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
        var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
        foreach (TKey key in Keys)
        {
            writer.WriteStartElement("item");
            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();
            writer.WriteStartElement("value");
            var value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    }

    public SerializableDictionary() : base()
    {
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary)
    {
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer)
    {
    }

    public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer)
    {
    }

    public SerializableDictionary(int capacity) : base(capacity)
    {
    }

    public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer)
    {
    }

}