一旦程序员决定实施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>
答案 0 :(得分:92)
是的,GetSchema()should return null。
IXmlSerializable.GetSchema方法这个 方法是保留的,不应该 用过的。实施时 IXmlSerializable接口,你应该 返回null引用(Nothing in Visual Basic)来自这个方法,相反, 如果指定自定义架构是 要求,申请 XmlSchemaProviderAttribute到 类。
对于read和write,对象元素已经被写入,因此您不需要在write中添加外部元素。例如,您可以开始在两者中读/写属性。
WriteXml实现你 提供应该写出XML 对象的表示。该 框架写一个包装元素和 将XML编写器放在其后面 开始。您的实施可能会写 其内容,包括儿童 元素。然后框架关闭 包装元素。
对于read:
ReadXml方法必须重新构建 你的对象使用的信息 是由WriteXml方法编写的。
当调用此方法时,读者 位于开头 包装信息的元素 你的类型。也就是说,就在之前 开始标记,指示开头 一个序列化对象。当这个 方法返回,它必须已经读取了 整个元素从头到尾, 包括其所有内容。不像 WriteXml方法,框架 不处理包装元素 自动。你的实施 必须这样做。没有观察到这些 定位规则可能导致代码 生成意外运行时异常 或损坏的数据。
我同意这有点不清楚,但归结为“Read()
包装器的end-element标签是你的工作。”
答案 1 :(得分:32)
我用样本写了一篇关于这个主题的文章,因为MSDN文档到目前为止还不太清楚,你可以在网上找到的例子大部分都是错误实现的。
陷阱是处理Marc Gravell已经提到的区域和空元素。
http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx
答案 2 :(得分:8)
我们的解决方案是定义我们自己的IXmlSerializable
接口,该接口派生自系统1,它添加了一个名为WriteOuterXml()
的方法。你可以猜到,这个方法只是编写外部元素,然后调用WriteXml()
,然后写出元素的结尾。当然,系统XML序列化程序不会调用此方法,因此它只在我们自己进行序列化时才有用,因此在您的情况下可能有用也可能没用。同样,我们添加了一个ReadContentXml()
方法,它不读取外部元素,只读取其内容。
答案 3 :(得分:2)
如果您已经拥有了类的XmlDocument表示或者更喜欢使用XML结构的XmlDocument方法,那么实现IXmlSerializable的快速而简单的方法就是将此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);
}
}
答案 4 :(得分:0)
其他答案涵盖了接口实现,但我想以2美分的价格替换根元素。
我过去了解到更喜欢将根元素用作元数据。这有一些好处:
以下是可序列化字典的示例,其中以这种方式定义字典根元素:
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)
{
}
}