Xml Serialization Inside a Collection

XmlRoot does not seem to work with the classes contained in the collection. Here are the classes that I defined:

 [XmlRoot("cars")] public class CarCollection : Collection<Car> { } [XmlRoot("car")] public class Car { [XmlAttribute("make")] public String Make { get; set; } [XmlAttribute("model")] public String Model { get; set; } } 

Here is the code that I use to serialize these objects:

  CarCollection cars = new CarCollection(); cars.Add(new Car { Make = "Ford", Model = "Mustang" }); cars.Add(new Car { Make = "Honda", Model = "Accord" }); cars.Add(new Car { Make = "Toyota", Model = "Tundra" }); using (MemoryStream memoryStream = new MemoryStream()) { XmlSerializer carSerializer = new XmlSerializer(typeof(CarCollection)); carSerializer.Serialize(memoryStream, cars); memoryStream.Position = 0; String xml = null; using (StreamReader reader = new StreamReader(memoryStream)) { xml = reader.ReadToEnd(); reader.Close(); } memoryStream.Close(); } 

The xml after serialization is as follows:

 <cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Car make="Ford" model="Mustang" /> <Car make="Honda" model="Accord" /> <Car make="Toyota" model="Tundra" /> </cars> 

Please note that the β€œC” in the car is not lowercase. What do I need to change for this to happen? If I serialize the car itself, it comes out as I would expect.

UPDATE: I found another workaround. I'm not sure how much I like it, but it will work for my cause. If I create a custom class (see below) and get a CarCollection, the serialization works as I expected.

  public class XmlSerializableCollection<T> : Collection<T>, IXmlSerializable { public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) { return; } XmlSerializer serializer = new XmlSerializer(typeof(T)); while (reader.NodeType != XmlNodeType.EndElement) { T t = (T)serializer.Deserialize(reader); this.Add(t); } if (reader.NodeType == XmlNodeType.EndElement) { reader.ReadEndElement(); } } public void WriteXml(XmlWriter writer) { XmlSerializer reqSerializer = new XmlSerializer(typeof(T)); foreach (T t in this.Items) { reqSerializer.Serialize(writer, t); } } } 
+4
source share
3 answers

Can't you just mark up a car using XmlType?

 [XmlType("car")] public class Car { } 
+1
source

XmlRootAttribute is applied only if the element is the root of serialization of the object graph, i.e. The object you are passing to the XmlSerializer instance.

To control the serialization of collections, you typically use XmlElementAttribute to specify the name of the element to serialize the children of. Unfortunately, this attribute can only be applied to a field or property, and not to a class.

If you can represent your collection as a property of a class, you can use this attribute as follows:

 [XmlRoot("cars")] public class CarList { [XmlElement("car")] public CarCollection Cars { get; set; } } 

With your sample code, you will get the following result:

 <cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <car make="Ford" model="Mustang" /> <car make="Honda" model="Accord" /> <car make="Toyota" model="Tundra" /> </cars> 

This is a bit of a workaround, but it is the closest you can get without a lot of custom code.

+2
source

This may be a copy, but I was able to do this work with the DataContractSerializer as follows:

 using System; using System.IO; using System.Collections.ObjectModel; using System.Runtime.Serialization; class Program { static void Main() { CarCollection cars = new CarCollection(); cars.Add(new Car { Make = "Ford", Model = "Mustang" }); cars.Add(new Car { Make = "Honda", Model = "Accord" }); cars.Add(new Car { Make = "Toyota", Model = "Tundra" }); using (MemoryStream memoryStream = new MemoryStream()) { DataContractSerializer serializer = new DataContractSerializer(typeof(CarCollection)); serializer.WriteObject(memoryStream, cars); memoryStream.Position = 0; String xml = null; using (StreamReader reader = new StreamReader(memoryStream)) { xml = reader.ReadToEnd(); reader.Close(); } memoryStream.Close(); } } } [CollectionDataContract(Name = "cars")] public class CarCollection : Collection<Car> { } [DataContract(Name = "car")] public class Car { [DataMember(Name = "make")] public String Make { get; set; } [DataMember(Name = "model")] public String Model { get; set; } } 

Output:

 <cars xmlns="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <car> <make>Ford</make> <model>Mustang</model> </car> <car> <make>Honda</make> <model>Accord</model> </car> <car> <make>Toyota</make> <model>Tundra</model> </car> </cars> 

Note that the attributes of your types have been changed to support the use of the DataContractSerializer . I don’t know if this is the direction you want to go, but I found that in almost all cases I prefer to use the DataContractSerializer instead of the older XmlSerializer .

+1
source

All Articles