XML Serialize generic list of serializable objects?

See Introducing XML Serialization: Items That Can Be Serialized The following items can be serialized using the XmLSerializer class: Public read/write properties and fields of public classes Classes that implement ICollection or IEnumerable XmlElement objects XmlNode objects DataSet objects In particular, ISerializable or the Serializable attribute does not matter. Now that you've told us what your problem is ("it doesn't work" is not a problem statement), you can get answers to your actual problem, instead of guesses. When you serialize a collection of a type, but will actually be serializing a collection of instances of derived types, you need to let the serializer know which types you will actually be serializing.

This is also true for collections of object. You need to use the XmlSerializer(Type,Type) constructor to give the list of possible types.

1 for the remark about ISerializable and Serializable – Thomas Levesque Jul 31 '09 at 14:52 msdn.microsoft. Com/en-us/library/182eeyhh%28VS.85%29. Aspx - link got mangled, thanks for this I'm understanding more...lots to read.

– Simon D Jul 31 '09 at 15:04 Also thanks for the advice on how to ask questions, it's appreciated. – Simon D Jul 31 '09 at 15:09 +1. Yes, they don't matter for XML, but if he ever wants to use other types of serialization, they will.

Also, since he was casting to ISerializable, it seemed like a perfectly basic question for him to check that the different types implemented that interface. I hope it wasn't a dig at my comment, and I think this was helpful in clarifying some of these details. – Erich Mirabal Jul 31 '09 at 15:12 @Erich: don't be so sensitive.It's a fact, not a dig.

I'm constantly seeing XML Serialization examples with Serializable, which is just plain wrong. – John Saunders Jul 31 '09 at 15:19.

I have an solution for a generic List with dynamic binded items. Class PersonalList it's the root element XmlRoot("PersonenListe") XmlInclude(typeof(Person)) // include type class Person public class PersonalList { XmlArray("PersonenArray") XmlArrayItem("PersonObjekt") public List Persons = new List(); 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 it's an single list element 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; } } class SpecialPerson 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; } } class SuperPerson inherits Person XmlType("SuperPerson") // define Type public class SuperPerson : Person { XmlArray("Skills") XmlArrayItem("Skill") public List Skills { get; set; } XmlElement("Alias") public string Alias { get; set; } public SuperPerson() { Skills = new List(); } public SuperPerson(string name, string city, int age, string id, string skills, string alias) { Skills = new List(); 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 the 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(); } Important is the definition and includes of the diffrent types.

I think Dreas' approach is ok. An alternative to this however is to have some static helper methods and implement IXmlSerializable on each of your methods e. G an XmlWriter extension method and the XmlReader one to read it back.

Public static void SaveXmlSerialiableElement(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(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 do go down the route of using the XmlSerializer class directly, create serialization assemblies before hand if possible, as you can take a large performance hit in constructing new XmlSerializers regularly. For a collection you need something like this: public static void SaveXmlSerialiazbleCollection(this XmlWriter writer, String collectionName, String elementName, IEnumerable 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(); }.

This looks interesting, but I'm not sure how to use it exactly. I'll try to play with it a bit and see if I can get it to work. – Simon D Jul 31 '09 at 14:32 Ok, firstly it all the objects in the collection need to be strongly typed to the same base type.

The objects need to implement IXmlSerializable (not too hard). For a collection e.g. List you'll then call something like: XmlWriter. SaveXmlSerializableCollection("item", this.

CollectionOfTs) – Ian Jul 31 '09 at 14:37 btw, do you need to deserialize them too? If not I can probably provide a fuller example for you... – Ian Jul 31 '09 at 14:41 I probably will deserialize, but that's already a fair bit to be getting on with. I'll try and put what you've given me already into practice.

Thanks very much. – Simon D Jul 31 '09 at 14:55.

I think it's best if you use methods with generic arguments, like the following : public static void SerializeToXml(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(string xml) { T result; var ser = new XmlSerializer(typeof(T)); using (var tr = new StringReader(xml)) { result = (T)ser.

Deserialize(tr); } return result; }.

OK perhaps, but this still doesn't do what I want - serialize a list of objects, possibly of different types. I want to be able to add a bunch of objects I want to save to a list, then serialize the entire list in one go (and to one file). – Simon D Jul 31 '09 at 14:20 I don't like this option, because creating XmlSerializers is expensive.

– Ian Jul 31 '09 at 14:32 1 paradisonoir, construction of them creates temporary serialization assemblies for the given type (and all referenced types that need to be deserialized). If you have a large object graoh that needs serializing or just a large number of objects and you're repeatedly calling XmlSerializer then it can be slow. We found that it was taking 10-13 seconds to serialize and deserialize some settings, which was made almost instant by pre-building these serialization assemblies.

– Ian Jul 31 '09 at 14:55 1 @Fred the XmlSerializer(Type) constructor creates a temp assembly to handle the serialization for that type and adds it to the AppDomain, but if you call it a second time for the same type, it does not re-use the same temp assembly, it creates another one. Since assemblies cannot be removed from the AppDomain once they are loaded, every time you invoke the constructor your memory footprint will increase slightly. – Rex M Jul 31 '09 at 14:59 1 In an application like the one I work on daily, if each new temp assembly represented 1kb, we would be leaking 200 gigabytes per day.By carefully managing the XmlSerializers in a type cache, we keep the same set of serializers in memory, at a total cost of about 1 megabyte of cache.

– Rex M Jul 31 '09 at 15:05.

Below is a Util class in my project: namespace Utils { public static class SerializeUtil { public static void SerializeToFormatter(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(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(string path, object obj) { XmlSerializer xs = new XmlSerializer(typeof(T)); using (FileStream fs = File. Create(path)) { xs.

Serialize(fs, obj); } } public static T DeserializeFromXML(string path) { XmlSerializer xs = new XmlSerializer(typeof(T)); using (FileStream fs = File. OpenRead(path)) { return (T)xs. Deserialize(fs); } } public static T DeserializeFromXml(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); } } } }.

Here's a related question on how to (de)serialize a collection of objects that belong to classes deriving from the same base type.

You can't serialize a collection of objects without specifying the expected types. You must pass the list of expected types to the constructor of XmlSerializer (the extraTypes parameter) : List list = new List(); 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 of your list inherit from the same class, you can also use the XmlInclude attribute to specify the expected types : XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar)) public class MyBaseClass { }.

Hmm, that looks interesting. I'm assuming though it still won't deserialize? – Ian Jul 31 '09 at 15:05 It will deserialize.

Actually, during serialization, an extra attribute xsi:type is written on the element that represents the object. This allows XmlSerializer to know the actual type of the object – Thomas Levesque Jul 31 '09 at 15:47.

If the XML output requirement can be changed you can always use binary serialization - which is better suited for working with heterogeneous lists of objects. Here's an example: private void SerializeList(List 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 SerializeMeBaby = new List { new Animal { Home = "London, UK" }, new Person { Name = "Skittles" } }; string TargetPath = Path. Combine( Environment. GetFolderPath(Environment.SpecialFolder.

ApplicationData), "Test1. Dat"); SerializeList(SerializeMeBaby, TargetPath); }.

Nice example. This is more what I was thinking - a number of different objects and I want to put them into one file. I prefer XML to binary though, but will try this.

– Simon D Jul 31 '09 at 15:24.

You have made the zero parameter constructor private. I'm pretty sure the serializer needs access to that constructor.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions