XmlSerializer序列化通用接口列表

我正在尝试使用XmlSerializer来持久化List(T),其中T是一个接口。 序列化器不喜欢接口。 我很好奇是否有一种简单的方法可以使用XmlSerializer轻松地序列化异构对象列表。 这就是我想要的:

public interface IAnimal { int Age(); } public class Dog : IAnimal { public int Age() { return 1; } } public class Cat : IAnimal { public int Age() { return 1; } } private void button1_Click(object sender, RoutedEventArgs e) { var animals = new List { new Dog(), new Cat() }; var x = new XmlSerializer(animals.GetType()); var b = new StringBuilder(); var w = XmlTextWriter.Create(b, new XmlWriterSettings { NewLineChars = "\r\n", Indent = true }); //FAIL - cannot serialize interface. Does easy way to do this exist? x.Serialize(w, animals); var s = b.ToString(); } 

您也可以使用XmlSerializer,但是您需要包含可能出现在您要序列化的对象图中的所有可能类型,这会限制可扩展性并降低可维护性。 你可以通过使用XmlSerializer的构造函数的重载来实现:

 var x = new XmlSerializer(animals.GetType(), new Type[] { typeof(Cat), typeof(Dog) }); 

此外,使用XmlSerializer时还有几个值得注意的问题, 这里列出了所有这些(MSDN) – 例如,在“动态生成的程序集”标题下查看。

XmlSerializer无法处理接口,因为它不知道在反序列化时要创建哪些类型。 要解决这个问题,您需要通过实现IXmlSerializable接口来自行处理序列化的这一部分。 这允许您记录类型,以便您可以重新创建(反序列化)它。

下面的ListOfIAnimal类显示了我如何inheritance和扩展通用列表List以实现所需的接口。 我蹲起你的旧类,为每个类添加一个额外的非接口字段,这样我就可以看到具体的类被正确地序列化和反序列化了。

与您的代码相比,我只是使用新类型ListOfIAnimal代替List ,其他更改只是一点点重构。

它的完整代码,只需将其复制到自己的.cs文件中,调用第一个函数来逐步执行它。

 using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; namespace Serialiser { static class SerialiseInterface { public static void SerialiseAnimals() { String finalXml; // Serialize { var animals = new ListOfIAnimal{ new Dog() { Age = 5, Teeth = 30 }, new Cat() { Age = 6, Paws = 4 } }; var xmlSerializer = new XmlSerializer(animals.GetType()); var stringBuilder = new StringBuilder(); var xmlTextWriter = XmlTextWriter.Create(stringBuilder, new XmlWriterSettings { NewLineChars = "\r\n", Indent = true }); xmlSerializer.Serialize(xmlTextWriter, animals); finalXml = stringBuilder.ToString(); } // Deserialise { var xmlSerializer = new XmlSerializer(typeof(ListOfIAnimal)); var xmlReader = XmlReader.Create(new StringReader(finalXml)); ListOfIAnimal animals = (ListOfIAnimal)xmlSerializer.Deserialize(xmlReader); } } } public class ListOfIAnimal : List, IXmlSerializable { public ListOfIAnimal() : base() { } #region IXmlSerializable public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { reader.ReadStartElement("ListOfIAnimal"); while (reader.IsStartElement("IAnimal")) { Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName")); XmlSerializer serial = new XmlSerializer(type); reader.ReadStartElement("IAnimal"); this.Add((IAnimal)serial.Deserialize(reader)); reader.ReadEndElement(); //IAnimal } reader.ReadEndElement(); //ListOfIAnimal } public void WriteXml(XmlWriter writer) { foreach (IAnimal animal in this) { writer.WriteStartElement("IAnimal"); writer.WriteAttributeString("AssemblyQualifiedName", animal.GetType().AssemblyQualifiedName); XmlSerializer xmlSerializer = new XmlSerializer(animal.GetType()); xmlSerializer.Serialize(writer, animal); writer.WriteEndElement(); } } #endregion } public interface IAnimal { int Age { get; set; } } public class Dog : IAnimal { public int Age { get; set;} public int Teeth { get; set;} } public class Cat : IAnimal { public int Age { get; set;} public int Paws { get; set;} } } 

我想把反序列化作为练习给读者,但如果没有它,代码就会非常有用。

你必须使用XmlSerializer吗? 这是XmlSerializer一个已知问题。

您可以使用BinaryFormatter保存到流:

 BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, animals); 

另一种方法是使用WCF的DataContractSerializer并使用KnownType属性提供类型。

您可以使用ExtendedXmlSerializer 。

 var serializer = new ExtendedXmlSerializer(); var xml = serializer.Serialize(animals); 

你的xml看起来像:

      

简单的方法是将[Serializable()]装饰添加到类中,并将IList更改为List,看看是否有效。

如果你使用接口,那么去看看webturner的答案。