Сериализация и наследование .NET XML
У меня такая структура:
public interface A
{
public void method();
}
public class B : A
{
}
public class C : A
{
}
List<A> list;
список содержит объекты типа B и C они также имеют некоторые поля, которые я хотел бы сохранить, могу ли я теперь сериализовать его, десериализовать обратно и получить правильные экземпляры объекта? Желательно в XML
изменить:
есть ли простой способ сериализовать этот список, содержащий интерфейсы, а затем десериализовать его обратно в экземпляры B и C?
6 ответов
вы можете попробовать использовать 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);
}
}
}
предполагая, что вы используете встроенную сериализацию .net XML, вы должны взглянуть на следующий атрибут:
System.Xml.Serialization.XmlIncludeAttribute
Это позволяет указать сериализатору включать другие типы при сериализации / десериализации.
добавление новых типов в список и не обновление метаданных сериализации является распространенным источником ошибок, убедитесь, что у вас есть адекватное покрытие тестирования.
Я бы использовал абстрактный класс вместо интерфейса (поскольку нельзя сериализовать тип интерфейса), а затем вместо жесткого кодирования типа с помощью атрибута XmlInclude я бы добавил известные типы В XmlSerializer в последовательных и Десериализованных методах, таких как:
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();
}
да, но вы должны играть с атрибутами 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;
}
}
}
EDIT: удалить атрибут сериализации, поскольку он не нужен (Но необходим в моем проекте, где код)
для вашего случая сделайте абстрактный класс, который реализует ваш интерфейс, например:
abstract class Abs : A
а затем выведите свои классы из Abs
public class B : Abs
public class C : Abs
и Список список;
теперь используйте XmlIncludeAttribute для добавления типов в массив типов XmlSerializer.
XmlSerializer
не работает с интерфейсами. Так что вы можете:
преобразовать интерфейс абстрактный класс, а затем использовать XmlIncludeAttribute
для него или предоставить KnownTypes в XmlSerializer
или
реализовать IXmlSerializable
для родительского типа
или
рассмотрите возможность использования DataContractSerializer
из .NET 3.0