LinkedList无法序列化?

时间:2010-03-20 13:55:03

标签: c# xml-serialization

以下是我的课程:http://pastebin.com/3dc5Vb1t

当我尝试运行时

BookStore b = new BookStore();
b.LoadFromXML(Server.MapPath("list.xml"));
Label1.Text = b.ToString();

我收到以下错误:

  

您必须在System.Collections.Generic.LinkedList`1 [[Book,App_Code.cxsacizw,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null]]上实现默认访问器,因为它继承自ICollection。

错误来源为XmlSerializer s = new XmlSerializer(typeof(BookStore));

当我试图在谷歌上寻找解决方案时,我发现LinkedList在序列化方面存在一些问题。我该如何处理?

非常感谢。

3 个答案:

答案 0 :(得分:5)

似乎不可能。

此处有错误报告:linkedlist-t-can-not-be-serialized-using-the-xmlserializer。在哪里可以阅读以下内容:

  

微软于11/11/2004发表于   19时35分
  我们选择不拥有索引器   LinkedList上的方法用于表现   原因。所以LinkedList不会   XMLSeriliazable。

答案 1 :(得分:4)

摘要:

某些.Net类型(如链接列表,哈希表(字典)等)在尝试序列化时会出现一些问题。这似乎主要是设计的:还有其他更简单的类型可以表示相同的值范围(例如,普通列表而不是链表,或者是对的列表而不是字典),所以.Net假定如果您使用的是更具体的类型,则需要其特定功能。如果无法序列​​化此类功能(例如,无法在XML中对哈希表进行描述),则会出现问题。

关键点在于:您是否真的需要在序列化表单上使用这些类型的特定功能?例如,如果您序列化链表以使序列化版本包含元素之间的链接,那么您将会遇到严重的问题。幸运的是,在大多数情况下,在实际使用对象时只需要特殊功能,因此可以序列化它的简化(但足够完整)版本,并在反序列化时重建高级对象。

为了实现上述目标,.Net包含了一些有用的工具来干预de /序列化过程。首先,您应该始终使用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”参数,都必须是可序列化的,但无法在代码中强制执行此类要求。

顺便说一句,让我处理一些合法的事情:作为上面代码段的作者,我授予任何人不得撤销的,非独占的全球许可,以便将其用于任何目的(包括但不是限制,创造任何形式的衍生作品,并以任何形式分发它们。归档不是必需的,但总是受欢迎。

哦,看了你的代码之后,我强烈建议你使用StringBuilder来实现你的ToString()方法:每次你的代码在String上调用+ =时,就会创建一个新的字符串对象(需要时间)和记忆)。虽然您不太可能因此而耗尽内存,但很长的列表可能很容易对您的应用程序产生性能影响。

希望这有帮助

答案 2 :(得分:3)

请参阅When should I use a List vs a LinkedList

如果您使用链接列表,可能会因为性能原因而假设您这样做。否则,列表&lt; T&gt;可能更合适。