在摘要:
一些.NET类型如链表,哈希表(字典)等有一些问题,当您尝试序列化。这似乎主要是通过设计:还有其他更简单的类型可以表示相同的值范围(例如,普通列表而不是链接列表,或者对的列表而不是字典),所以.Net假定如果您使用更具体的类型,则需要其特定功能。当这些功能无法序列化时(例如,不能用XML描述散列表),就会出现问题。
重点是:你真的需要这些类型的特定功能在他们的序列化的形式?例如,如果您要序列化一个链表,以便序列化版本包含元素之间的链接,那么您将会遇到严重的麻烦。幸运的是,在大多数情况下,只有在实际使用该对象时才需要特殊功能,因此您可以序列化一个简化的(但完整的)版本并在反序列化时重建高级对象。
为了实现上述可能性,.Net包含一些有用的工具来干涉反序列化过程。首先,您应该始终用System.SerializableAttribute(http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx)标记可序列化对象。接下来,您可以实现System.Runtime.Serialization.ISerializable(http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx)以完全控制序列化过程。在最简单的情况下,你需要做的就是将你的链表转换成普通的链表,并将它添加到你在GetObjectData(...)中得到的SerializationInfo参数作为一个单一值(我假设你将它标记为“值“)进行序列化。然后,要启用反序列化,请添加如下例所示的构造函数。
但是,这仅涵盖共享序列化基础结构。要完全控制XML序列化,您需要实现System.Xml.Serialization.IXmlSerializable。这样做时,请记住,编写器会隐式地将输出封装在表示被序列化的对象类型的元素中;读者需要明确地挖掘该元素(在某些情况下,可能需要此不对称)。如果你不习惯,实现这个接口可能会很棘手。Net的XML流;但幸运的是,我不得不在前一段时间对字典做类似的事情,并且我可以回收大部分代码;)。
具体到: 本示例提供LinkedList序列化为“普通”列表的基本要素,并将其反序列化回链接列表。序列化表格不是包含元素间链接;但这些链接在反序列化后可靠地重新制作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Xml;
using System.IO;
namespace WFTest {
[Serializable]
class SerializableLinkedList<T>: LinkedList<T>, ISerializable, IXmlSerializable {
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
info.AddValue("value", this.ToList());
}
// Implied by ISerializable, but interfaces can't actually define constructors:
SerializableLinkedList(SerializationInfo info, StreamingContext context)
: base((IEnumerable<T>)info.GetValue("value", typeof(List<T>))) { }
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { return null; }
void IXmlSerializable.ReadXml(XmlReader reader) {
this.Clear(); // Start with an empty list
reader.ReadStartElement(); // Skips the opening tag
while (reader.LocalName=="item") { // Retrieve all elements:
T value;
if(reader.IsEmptyElement) { // If element is empty...
value=default(T); // the item's value falls back to default(T)
reader.ReadStartElement(); // and consume the (empty) element
} else {
// IIRC, ReadInnerXml() consumes the outer tag, despite not returning them.
value=(T)((new XmlSerializer(typeof(T))).Deserialize(new StringReader(reader.ReadInnerXml())));
}
this.AddLast(value);
}
reader.ReadEndElement(); // Consumes the remaining closing tag from the reader
}
void IXmlSerializable.WriteXml(XmlWriter writer) {
foreach(T item in this) {
// Format the item itself:
StringBuilder sb=new StringBuilder();
(new XmlSerializer(typeof(T))).Serialize(XmlWriter.Create(sb), item);
XmlDocument doc=new XmlDocument();
doc.LoadXml(sb.ToString());
// and now write it to the stream within <item>...</item> tags
writer.WriteStartElement("item");
writer.WriteRaw(doc.DocumentElement.OuterXml);
writer.WriteEndElement(); // </item>
}
}
}
}
使用这个类,而不是为对象的“原始” LinkedList类(或基类,如果你需要从LinkedList的派生),和系列化不应触发与列表任何更多的问题。但是,请注意,无论您用作此列表的“T”参数,它们本身都必须是可序列化的,但无法在代码中强制执行此类要求。作为上述代码片段的作者,我授予任何人以任何目的使用它的不可撤销的,非排他性的全球许可,包括但不限于不限于创作任何形式的衍生作品,并以任何形式发布)。归因不是必需的,但总是受欢迎的。
呵呵,看看你的代码后,我强烈建议你为你的ToString()方法的实现使用StringBuilder:每当你的代码在String上调用+ =时,就会创建一个新的字符串对象时间和记忆)。虽然不太可能会因此而耗尽内存,但是很长的列表可能会轻易地引发对应用程序性能的影响。
希望这有助于的
可能重复[如何将XML序列化的LinkedList?](http://stackoverflow.com/questions/2271582/how-to-xml-serialize-a-linkedlist) – nawfal 2014-07-16 21:46:13