.NET XML Сериализация и наследование
У меня есть структура вроде этого:
public interface A
{
public void method();
}
public class B : A
{
}
public class C : A
{
}
List<A> list;
Список содержит объекты типа B и C, они также имеют некоторые поля, которые я хотел бы сохранить, могу ли я теперь сериализовать его, десериализовать назад и получить соответствующие экземпляры объектов?
Предпочтительно XML
EDIT:
Есть ли простой способ сериализации этого списка, который содержит интерфейсы, а затем десериализовать его обратно в экземпляры B и C?
Ответы
Ответ 1
Вы можете попробовать использовать DataContractSerializer:
public interface A
{
}
public class B : A
{
}
public class C : A
{
}
class Program
{
static void Main(string[] args)
{
List<A> list = new List<A>(new A[] { new B(), new C() });
var serializer = new DataContractSerializer(
list.GetType(), new[] { typeof(B), typeof(C) });
var sb = new StringBuilder();
using (var stringWriter = new StringWriter(sb))
using (var writer = XmlWriter.Create(stringWriter))
{
serializer.WriteObject(writer, list);
}
using (var stringReader = new StringReader(sb.ToString()))
using (var reader = XmlReader.Create(stringReader))
{
list = (List<A>)serializer.ReadObject(reader);
}
}
}
Ответ 2
Предполагая, что вы используете встроенную сериализацию .net XML, вы должны взглянуть на следующий атрибут:
System.Xml.Serialization.XmlIncludeAttribute
Он позволяет вам инструктировать сериализатор включать другие типы при сериализации/десериализации.
Добавление новых типов в список, а не обновление метаданных сериализации - общий источник ошибок, убедитесь, что у вас есть достаточное покрытие для тестирования.
Ответ 3
Я бы использовал абстрактный класс вместо интерфейса (так как нельзя сериализовать тип интерфейса), а вместо жесткого кодирования типа с использованием атрибута XmlInclude я бы добавил известные типы в XmlSerializer в Serial и Deserialize такие методы:
string listXml = Serialize<List<A>>(ListA, new Type[] { typeof(B), typeof(C) });
List<IA> NewList = Deserialize<List<A>>(listXml, new Type[] { typeof(B), typeof(C) });
private static T Deserialize<T>(string Xml, Type[] KnownTypes)
{
XmlSerializer xs = new XmlSerializer(typeof(T),KnownTypes);
StringReader sr = new StringReader(Xml);
return (T)xs.Deserialize(sr);
}
private static string Serialize<T>(Object obj, Type[] KnownTypes)
{
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
XmlSerializer xs = new XmlSerializer(typeof(T), KnownTypes);
xs.Serialize(sw, obj);
}
return sb.ToString();
}
Ответ 4
Да, но вам нужно играть с атрибутами XmlElement, XmlRoot и XmlArray.
Каждому типу требуется его собственное имя элемента.
EDIT: некоторый пример кода. Все классы производятся из общего базового класса.
Вот пример кода:
[XmlRoot(ElementName="Root")]
public sealed class SomeObject
{
private BaseObject _Object;
[XmlElement(Type=typeof(App.Projekte.Projekt), ElementName="Projekt")]
[XmlElement(Type=typeof(App.Projekte.Task), ElementName="Task")]
[XmlElement(Type=typeof(App.Projekte.Mitarbeiter), ElementName="Mitarbeiter")]
public BaseObject Object
{
get
{
return _Object;
}
set
{
_Object = value;
}
}
}
РЕДАКТИРОВАТЬ: удалить атрибут Serialization, поскольку он не нужен (но необходим в моем проекте, где находится код)
Ответ 5
В вашем случае создайте абстрактный класс, который реализует ваш интерфейс, например:
abstract class Abs : A
а затем выведите свои классы из Abs
public class B : Abs
public class C : Abs
и Список списка;
теперь используйте XmlIncludeAttribute, чтобы добавить ваши типы в массив типа XmlSerializer.
Ответ 6
XmlSerializer
не работает с интерфейсами. Таким образом, вы можете:
Преобразовать интерфейс в абстрактный класс, а затем использовать XmlIncludeAttribute
для него или предоставить KnownTypes для XmlSerializer
или
Внедрить IXmlSerializable
для родительского типа
или
Рассмотрите возможность использования DataContractSerializer
из .NET 3.0