XML序列化可序列化对象的通用列表

我可以序列化一个可序列化对象的通用列表,而不必指定它们的types。

类似于下面破碎的代码背后的意图:

List<ISerializable> serializableList = new List<ISerializable>(); XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType()); serializableList.Add((ISerializable)PersonList); using (StreamWriter streamWriter = System.IO.File.CreateText(fileName)) { xmlSerializer.Serialize(streamWriter, serializableList); } 

编辑:

对于那些想知道详细信息的人:当我尝试运行这个代码时,它在XMLSerializer行上的错误是:

无法序列化接口System.Runtime.Serialization.ISerializable。

如果我更改为List<object> "There was an error generating the XML document."得到"There was an error generating the XML document." 。 InnerException细节为"{"The type System.Collections.Generic.List1[[Project1.Person, ConsoleFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] may not be used in this context."}"

人物对象定义如下:

 [XmlRoot("Person")] public class Person { string _firstName = String.Empty; string _lastName = String.Empty; private Person() { } public Person(string lastName, string firstName) { _lastName = lastName; _firstName = firstName; } [XmlAttribute(DataType = "string", AttributeName = "LastName")] public string LastName { get { return _lastName; } set { _lastName = value; } } [XmlAttribute(DataType = "string", AttributeName = "FirstName")] public string FirstName { get { return _firstName; } set { _firstName = value; } } } 

PersonList只是一个List<Person>

这只是为了testing,所以没有觉得细节太重要了。 关键是我有一个或多个不同的对象,所有这些对象都是可序列化的。 我想将它们全部序列化为一个文件。 我认为最简单的方法就是把它们放在一个通用的列表中,并且一次将这个列表序列化。 但是这不起作用。

我也试过用List<IXmlSerializable> ,但是失败了

System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.

对不起,由于缺乏细节,但我是一个初学者,不知道需要什么细节。 如果有人要求更多的细节,试图以一种让我理解需要什么细节的方式作出回应,或者提供一个概括可能方向的基本答案,这将会很有帮助。

感谢我迄今得到的两个答案 – 我可以花更多的时间阅读而不会得到这些想法。 这是惊人的,如何有帮助的人在这个网站上。

我有一个通用的列表<>与dynamic绑定项目的解决scheme。

class PersonalList是根元素

 [XmlRoot("PersonenListe")] [XmlInclude(typeof(Person))] // include type class Person public class PersonalList { [XmlArray("PersonenArray")] [XmlArrayItem("PersonObjekt")] public List<Person> Persons = new List<Person>(); [XmlElement("Listname")] public string Listname { get; set; } // Konstruktoren public PersonalList() { } public PersonalList(string name) { this.Listname = name; } public void AddPerson(Person person) { Persons.Add(person); } } 

class Person它是一个单独的列表元素

 [XmlType("Person")] // define Type [XmlInclude(typeof(SpecialPerson)), XmlInclude(typeof(SuperPerson))] // include type class SpecialPerson and class SuperPerson public class Person { [XmlAttribute("PersID", DataType = "string")] public string ID { get; set; } [XmlElement("Name")] public string Name { get; set; } [XmlElement("City")] public string City { get; set; } [XmlElement("Age")] public int Age { get; set; } // Konstruktoren public Person() { } public Person(string name, string city, int age, string id) { this.Name = name; this.City = city; this.Age = age; this.ID = id; } } 

类SpecialPersoninheritancePerson

 [XmlType("SpecialPerson")] // define Type public class SpecialPerson : Person { [XmlElement("SpecialInterests")] public string Interests { get; set; } public SpecialPerson() { } public SpecialPerson(string name, string city, int age, string id, string interests) { this.Name = name; this.City = city; this.Age = age; this.ID = id; this.Interests = interests; } } 

类SuperPersoninheritancePerson

 [XmlType("SuperPerson")] // define Type public class SuperPerson : Person { [XmlArray("Skills")] [XmlArrayItem("Skill")] public List<String> Skills { get; set; } [XmlElement("Alias")] public string Alias { get; set; } public SuperPerson() { Skills = new List<String>(); } public SuperPerson(string name, string city, int age, string id, string[] skills, string alias) { Skills = new List<String>(); this.Name = name; this.City = city; this.Age = age; this.ID = id; foreach (string item in skills) { this.Skills.Add(item); } this.Alias = alias; } } 

和主要的testing来源

 static void Main(string[] args) { PersonalList personen = new PersonalList(); personen.Listname = "Friends"; // normal person Person normPerson = new Person(); normPerson.ID = "0"; normPerson.Name = "Max Man"; normPerson.City = "Capitol City"; normPerson.Age = 33; // special person SpecialPerson specPerson = new SpecialPerson(); specPerson.ID = "1"; specPerson.Name = "Albert Einstein"; specPerson.City = "Ulm"; specPerson.Age = 36; specPerson.Interests = "Physics"; // super person SuperPerson supPerson = new SuperPerson(); supPerson.ID = "2"; supPerson.Name = "Superman"; supPerson.Alias = "Clark Kent"; supPerson.City = "Metropolis"; supPerson.Age = int.MaxValue; supPerson.Skills.Add("fly"); supPerson.Skills.Add("strong"); // Add Persons personen.AddPerson(normPerson); personen.AddPerson(specPerson); personen.AddPerson(supPerson); // Serialize Type[] personTypes = { typeof(Person), typeof(SpecialPerson), typeof(SuperPerson) }; XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes); FileStream fs = new FileStream("Personenliste.xml", FileMode.Create); serializer.Serialize(fs, personen); fs.Close(); personen = null; // Deserialize fs = new FileStream("Personenliste.xml", FileMode.Open); personen = (PersonalList)serializer.Deserialize(fs); serializer.Serialize(Console.Out, personen); Console.ReadLine(); } 

重要的是定义和包括不同types。

请参阅XML序列化简介 :

可以序列化的项目

以下项目可以使用XmlSerializer类进行序列化:

  • 公共读/写属性和公共类的字段
  • 实现ICollectionIEnumerable
  • XmlElement对象
  • XmlNode对象
  • DataSet对象

特别是, ISerializable[Serializable]属性无关紧要。


现在你已经告诉我们你的问题是什么(“不起作用”不是一个问题陈述),你可以得到你的实际问题的答案,而不是猜测。

当你序列化一个types的集合,但实际上是序列化派生types实例的集合时,你需要让序列化器知道你实际上将要序列化的types。 对于object集合也是如此。

您需要使用XmlSerializer(Type,Type [])构造函数来给出可能的types列表。

您不能序列化对象的集合而不指定预期的types。 您必须将预期types的​​列表传递给XmlSerializer的构造函数( extraTypes参数):

 List<object> list = new List<object>(); list.Add(new Foo()); list.Add(new Bar()); XmlSerializer xs = new XmlSerializer(typeof(object), new Type[] {typeof(Foo), typeof(Bar)}); using (StreamWriter streamWriter = System.IO.File.CreateText(fileName)) { xmlSerializer.Serialize(streamWriter, list); } 

如果列表中的所有对象都从同一个类inheritance,那么也可以使用XmlInclude属性指定预期的types:

 [XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))] public class MyBaseClass { } 

我认为最好的方法是使用generics参数,如下所示:

 public static void SerializeToXml<T>(T obj, string fileName) { using (var fileStream = new FileStream(fileName, FileMode.Create)) { var ser = new XmlSerializer(typeof(T)); ser.Serialize(fileStream, obj); } } public static T DeserializeFromXml<T>(string xml) { T result; var ser = new XmlSerializer(typeof(T)); using (var tr = new StringReader(xml)) { result = (T)ser.Deserialize(tr); } return result; } 

我认为Dreas的方法是可以的。 然而,替代方法是使用一些静态帮助器方法,并在每个方法上实现IXmlSerializable,例如一个XmlWriter扩展方法和一个XmlReader方法来读取它。

 public static void SaveXmlSerialiableElement<T>(this XmlWriter writer, String elementName, T element) where T : IXmlSerializable { writer.WriteStartElement(elementName); writer.WriteAttributeString("TYPE", element.GetType().AssemblyQualifiedName); element.WriteXml(writer); writer.WriteEndElement(); } public static T ReadXmlSerializableElement<T>(this XmlReader reader, String elementName) where T : IXmlSerializable { reader.ReadToElement(elementName); Type elementType = Type.GetType(reader.GetAttribute("TYPE")); T element = (T)Activator.CreateInstance(elementType); element.ReadXml(reader); return element; } 

如果你直接使用XmlSerializer类的路线,如果可能的话,事先创build序列化程序集,因为定期构build新的XmlSerializers可能会带来很大的性能提升。

对于一个集合,你需要这样的东西:

 public static void SaveXmlSerialiazbleCollection<T>(this XmlWriter writer, String collectionName, String elementName, IEnumerable<T> items) where T : IXmlSerializable { writer.WriteStartElement(collectionName); foreach (T item in items) { writer.WriteStartElement(elementName); writer.WriteAttributeString("TYPE", item.GetType().AssemblyQualifiedName); item.WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); } 

下面是我的项目中的一个Util类:

 namespace Utils { public static class SerializeUtil { public static void SerializeToFormatter<F>(object obj, string path) where F : IFormatter, new() { if (obj == null) { throw new NullReferenceException("obj Cannot be Null."); } if (obj.GetType().IsSerializable == false) { // throw new } IFormatter f = new F(); SerializeToFormatter(obj, path, f); } public static T DeserializeFromFormatter<T, F>(string path) where F : IFormatter, new() { T t; IFormatter f = new F(); using (FileStream fs = File.OpenRead(path)) { t = (T)f.Deserialize(fs); } return t; } public static void SerializeToXML<T>(string path, object obj) { XmlSerializer xs = new XmlSerializer(typeof(T)); using (FileStream fs = File.Create(path)) { xs.Serialize(fs, obj); } } public static T DeserializeFromXML<T>(string path) { XmlSerializer xs = new XmlSerializer(typeof(T)); using (FileStream fs = File.OpenRead(path)) { return (T)xs.Deserialize(fs); } } public static T DeserializeFromXml<T>(string xml) { T result; var ser = new XmlSerializer(typeof(T)); using (var tr = new StringReader(xml)) { result = (T)ser.Deserialize(tr); } return result; } private static void SerializeToFormatter(object obj, string path, IFormatter formatter) { using (FileStream fs = File.Create(path)) { formatter.Serialize(fs, obj); } } } } 

最简单的方法来做到这一点,我发现..应用System.Xml.Serialization.XmlArray属性。

 [System.Xml.Serialization.XmlArray] //This is the part that makes it work List<object> serializableList = new List<object>(); XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType()); serializableList.Add(PersonList); using (StreamWriter streamWriter = System.IO.File.CreateText(fileName)) { xmlSerializer.Serialize(streamWriter, serializableList); } 

序列化器将select它作为一个数组,并将列表的项目作为子节点序列化。

knowTypeList参数让我们用DataContractSerializer序列化几种已知types:

 private static void WriteObject( string fileName, IEnumerable<Vehichle> reflectedInstances, List<Type> knownTypeList) { using (FileStream writer = new FileStream(fileName, FileMode.Append)) { foreach (var item in reflectedInstances) { var serializer = new DataContractSerializer(typeof(Vehichle), knownTypeList); serializer.WriteObject(writer, item); } } } 

如果可以更改XML输出要求,则始终可以使用二进制序列化 – 这更适合处理异构对象列表。 这是一个例子:

 private void SerializeList(List<Object> Targets, string TargetPath) { IFormatter Formatter = new BinaryFormatter(); using (FileStream OutputStream = System.IO.File.Create(TargetPath)) { try { Formatter.Serialize(OutputStream, Targets); } catch (SerializationException ex) { //(Likely Failed to Mark Type as Serializable) //... } } 

用途如下:

 [Serializable] public class Animal { public string Home { get; set; } } [Serializable] public class Person { public string Name { get; set; } } public void ExampleUsage() { List<Object> SerializeMeBaby = new List<Object> { new Animal { Home = "London, UK" }, new Person { Name = "Skittles" } }; string TargetPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Test1.dat"); SerializeList(SerializeMeBaby, TargetPath); }