正确的方法来实现IXmlSerializable?

一旦程序员决定实现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> 

是的,GetSchema() 应该返回null 。

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

对于读取和写入,对象元素已经被写入,所以你不需要在写入中添加外部元素。 例如,您可以开始读取/写入两个属性。

写 :

您提供的WriteXml实现应该写出对象的XML表示forms。 框架编写一个包装器元素,并在启动后定位XML编写器。 您的实现可能会写入其内容,包括子元素。 框架然后closures包装器元素。

并阅读 :

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

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

我会同意这一点有点不清楚,但归结为“这是你的工作Read()包装的结束元素标签”。

我用样本写了一篇关于这个主题的文章,因为MSDN文档现在还不太清楚,你可以在网上find的例子大部分都是错误地实现的。

缺点是除了Marc Gravell已经提到过的地方和空的元素。

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

是的,整个事情是一个雷区,不是吗? Marc Gravell的回答几乎涵盖了它,但是我想补充一点,在我所做的一个项目中,我们发现手动编写外部XML元素非常尴尬。 这也导致相同types的对象的XML元素名称不一致。

我们的解决scheme是定义我们自己的IXmlSerializable接口,从系统派生,它添加了一个名为WriteOuterXml()的方法。 正如你所猜测的,这个方法只是简单地写出外部元素,然后调用WriteXml() ,然后写入元素的结尾。 当然,系统XML序列化程序不会调用这个方法,所以只有在我们自己的序列化时才有用,所以对你的情况可能有帮助,也可能没有帮助。 同样,我们添加了一个ReadContentXml()方法,它不读取外部元素,只读取其内容。

如果你已经有了你的类的XmlDocument表示,或者更喜欢使用XML结构的XmlDocument方法,那么实现IXmlSerializable的一个快速和肮脏的方法就是将这个xmldoc传递给各个函数。

警告:XmlDocument(和/或XDocument)比xmlreader / writer慢一个数量级,所以如果性能是一个绝对的要求,这个解决scheme是不适合你的!

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