实现 IXmlSerializer 的正确方法?

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

是的,GetSchema () 应该返回 null

IXmlSerializable.GetSchema 方法 方法是保留的,不应该是 在实施 IXmlSerializer 接口,则应该 返回一个空引用( VisualBasic) ,而不是, 如果指定自定义架构是 如有需要,应用 控件的 XmlSchemaProviderAttribute 同学们。

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

对于 写作:

您的 WriteXml 实现 提供应写出 XML 对象的表示形式 框架写入一个包装器元素,然后 将 XML 编写器放在 开始。您的实现可以编写 其内容,包括儿童 元素。然后框架关闭 包装元素。

至于 :

ReadXml 方法必须重新构造 你的对象使用的信息 是由 WriteXml 方法编写的。

调用此方法时,读取器 定位在 元素,该元素包装 你喜欢的类型。也就是说,就在 表示开始的开始标记 序列化对象的 方法返回,它必须已经读取了 整个元素从头到尾, 包括它的所有内容。不像 WriteXml 方法、框架 不处理包装器元素 自动。你的执行 没有遵守这些规定 定位规则可能导致代码 生成意外的运行时异常 或者损坏数据。

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

我曾经写过一篇关于这个主题的文章,其中包含了一些例子,因为 MSDN 的文档现在还很不清楚,而且你可以在网上找到的例子大多数都是错误实现的。

缺陷是在 Marc Gravell 已经提到的内容之外处理场所和空元素。

Http://www.codeproject.com/kb/xml/implementixmlserializable.aspx

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

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

如果您已经有了类的 XmlDocument 表示形式,或者更喜欢使用 XmlDocument 处理 XML 结构的方式,那么实现 IXmlSerializer 的一种简单快捷的方法就是将这个 xmldoc 传递给各种函数。

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

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


}