XML Serialize a General List of Serializable Objects

Is it possible to serialize a general list of serializable objects without specifying their type.

Something like the intent behind the broken code below:

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); } 

Edit:

For those who wanted to know the details: when I try to run this code, these are errors in the XMLSerializer [...] line:

Cannot serialize the System.Runtime.Serialization.ISerializable interface.

If I go to List<object> , I get "There was an error generating the XML document." . The InnerException detail is "{"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."}"

The person object is defined as follows:

 [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 is just a List<Person> .

This is just for testing, so I didnโ€™t feel that the details were too important. The key is I have one or more different objects, all of which are serializable. I want to serialize them all into one file. I thought the easiest way to do this is to put them in a general list and serialize the list at a time. But that does not work.

I also tried with List<IXmlSerializable> but with an error

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

Sorry for the lack of details, but I'm new to this and don't know which part is required. It would be helpful if people asking in more detail would try to answer in such a way that I could understand what details are required, or a basic answer outlining possible directions.

Also, thanks to the two answers that I still have, I could spend a lot more time reading without getting these ideas. It's amazing how useful people are on this site.

+67
list generics c # xml-serialization
Jul 31 '09 at 14:08
source share
9 answers

I have a solution for a generic List <> with dynamic linked items.

the PersonalList class is the root element

 [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 A person is a single list item.

 [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; } } 

the SpecialPerson class inherits Person

 [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; } } 

SuperPerson class inherits Person

 [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; } } 

and main test source

 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(); } 

The definition is important and includes different types.

+65
Feb 25 '11 at 13:30
source share

See Introducing XML Serialization :

Elements that can be serialized

The following elements can be serialized using the XmlSerializer class:

  • General properties of reading and writing and fields of open classes
  • Classes That ICollection or IEnumerable
  • XmlElement objects
  • XmlNode objects
  • DataSet objects



In particular, the ISerializable or [Serializable] attribute does not matter.




Now that you have told us that your problem (โ€œthis does not workโ€ is not a problem), you can get answers to your actual problem, not guesswork.

When you serialize a type collection, but you actually serialize a collection of instances of derived types, you need to let the serializer know what types you will actually serialize. This is also true for object collections.

You need to use the XmlSerializer (Type, Type []) constructor to provide a list of possible types.

+20
Jul 31 '09 at 14:49
source share

You cannot serialize a collection of objects without specifying the expected types. You must pass the list of expected types to the XmlSerializer constructor ( extraTypes parameter):

 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); } 

If all the objects in your list are inherited from the same class, you can also use the XmlInclude attribute to indicate the expected types:

 [XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))] public class MyBaseClass { } 
+5
Jul 31 '09 at 14:58
source share

I think it is best to use methods with common arguments, for example:

 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; } 
+4
Jul 31 '09 at 14:16
source share

I think the Dreas approach is fine. An alternative to this, however, is to have some static helper methods and an IXmlSerializable implementation for each of your methods, such as the XmlWriter extension method, and the XmlReader to read it.

 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; } 

If you go down the path of using the XmlSerializer class directly, create a serialization assembly before the game, if possible, as you can greatly increase performance when creating new XmlSerializers.

For the collection you need something like this:

 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(); } 
+3
Jul 31 '09 at 14:21
source share

The following is the Util class in my project:

 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); } } } } 
+2
Mar 02 '10 at 5:59
source share

The easiest way to do this is what I found .. Apply the System.Xml.Serialization.XmlArray attribute to it.

 [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); } 

The serializer will pick an array on it and serialize the list items as child nodes.

+2
02 Oct '15 at 2:57
source share

If the XML output requirement can be changed, you can always use binary serialization, which is better for working with heterogeneous lists of objects. Here is an example:

 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) //... } } 

Use as such:

 [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); } 
0
Jul 31 '09 at 14:50
source share

knowTypeList allows you to serialize using the DataContractSerializer several well-known 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); } } } 
0
Apr 29 '15 at 20:48
source share



All Articles