В аннотации:
Некоторые типы .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), чтобы иметь полный контроль над процессом сериализации В простейшем случае все, что вам нужно сделать, - это преобразовать связанный список в обычный и добавить его к аргументу SerializationInfo, который вы получаете в GetObjectData (...), в качестве единого значения (я предполагаю, что вы пометите его как "value") для сериализации. Затем, чтобы включить десериализацию, добавьте конструктор, как в примере ниже.
Однако это касается только общей инфраструктуры сериализации. Чтобы получить полный контроль над сериализацией XML, вам необходимо реализовать System.Xml.Serialization.IXmlSerializable. При этом имейте в виду, что автор неявно обернет ваш вывод в элемент, который обозначает тип сериализуемого объекта; и читатель должен явно копаться в этом элементе (в некоторых случаях эта асимметрия может быть необходима). Может быть сложно реализовать этот интерфейс, если вы не привыкли к потокам XML .Net; но, к счастью для вас, мне пришлось делать что-то похожее со словарями, и я мог перерабатывать большую часть кода;).
К конкретному:
В этом примере представлены основные сведения о 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, создается новый строковый объект (что занимает много времени) и память). Хотя маловероятно, что вам не хватит памяти, этот длинный список может легко повлиять на производительность вашего приложения.
Надеюсь, это поможет