Проблема вирішена!
Добре, отже, нарешті я туди потрапив (правда, з великою допомогою звідси !).
Отже, підсумуйте:
Цілі:
- Я не хотів їхати маршрутом XmlInclude через головний біль головного болю.
- Як тільки рішення було знайдено, я хотів, щоб його швидко застосувати в інших додатках.
- Можуть використовуватися колекції абстрактних типів, а також окремі властивості абстрактних.
- Я не дуже хотів морочитися з тим, щоб робити конкретні речі на конкретних заняттях.
Визначені проблеми / зауваження:
- XmlSerializer робить досить прохолодне відображення, але воно дуже обмежене, коли йдеться про абстрактні типи (тобто він буде працювати лише з екземплярами самого абстрактного типу, а не з підкласами).
- Декоратори атрибутів Xml визначають, як XmlSerializer обробляє властивості, які знаходить. Також можна вказати фізичний тип, але це створює тісне зв’язок між класом та серіалізатором (не добре).
- Ми можемо реалізувати наш власний XmlSerializer, створивши клас, який реалізує IXmlSerializable .
Рішення
Я створив загальний клас, в якому ви вказуєте загальний тип як абстрактний тип, з яким будете працювати. Це дає класу можливість "перекладати" між абстрактним типом і конкретним типом, оскільки ми можемо жорстко кодувати кастинг (тобто ми можемо отримати більше інформації, ніж може XmlSerializer).
Потім я реалізував інтерфейс IXmlSerializable , це досить прямо, але при серіалізації нам потрібно переконатись, що ми записуємо тип конкретного класу в XML, щоб ми могли повернути його назад, коли десеріалізуємо. Важливо також зазначити, що він повинен бути повністю кваліфікованим, оскільки склади, в яких знаходяться два класи, можуть відрізнятися. Звичайно, тут є невелика перевірка типу та інше.
Оскільки XmlSerializer не може транслювати, нам потрібно надати код для цього, тому неявний оператор потім перевантажується (я навіть ніколи не знав, що ви можете це зробити!).
Код для AbstractXmlSerializer такий:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
namespace Utility.Xml
{
public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
{
public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
{
return o.Data;
}
public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
{
return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
}
private AbstractType _data;
public AbstractType Data
{
get { return _data; }
set { _data = value; }
}
public AbstractXmlSerializer()
{
}
public AbstractXmlSerializer(AbstractType data)
{
_data = data;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string typeAttrib = reader.GetAttribute("type");
if (typeAttrib == null)
throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because no 'type' attribute was specified in the XML.");
Type type = Type.GetType(typeAttrib);
if (type == null)
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the type specified in the XML was not found.");
if (!type.IsSubclassOf(typeof(AbstractType)))
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the Type specified in the XML differs ('" + type.Name + "').");
reader.ReadStartElement();
this.Data = (AbstractType)new
XmlSerializer(type).Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
Type type = _data.GetType();
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
new XmlSerializer(type).Serialize(writer, _data);
}
#endregion
}
}
Отже, як же сказати XmlSerializer працювати з нашим серіалізатором, а не за замовчуванням? Ми повинні передавати наш тип у властивості атрибутів Xml, наприклад:
[XmlRoot("ClassWithAbstractCollection")]
public class ClassWithAbstractCollection
{
private List<AbstractType> _list;
[XmlArray("ListItems")]
[XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))]
public List<AbstractType> List
{
get { return _list; }
set { _list = value; }
}
private AbstractType _prop;
[XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))]
public AbstractType MyProperty
{
get { return _prop; }
set { _prop = value; }
}
public ClassWithAbstractCollection()
{
_list = new List<AbstractType>();
}
}
Тут ви можете бачити, у нас є колекція та виставляється одна властивість, і все, що нам потрібно зробити, це додати параметр типу з іменем до декларації Xml, просто! : D
ПРИМІТКА. Якщо ви використовуєте цей код, я б дуже вдячний за вигук. Це також допоможе залучити більше людей до спільноти :)
Тепер, але не впевнений, що робити з відповідями тут, оскільки всі вони мали свої плюси і мінуси. Я модернізую ті, які я вважаю корисними (не ображаю тих, хто ними не був), і закрию це, коли отримаю представника :)
Цікава проблема та хороше задоволення для вирішення! :)