At work, I was stuck with a small problem when working with the XmlSerializer
which I have not been using that much of late. Anyway, I started out with something like this small demo program below:
class Program
{
static void Main(string[] args)
{
Order order = new Order
{
Products = new List<Product> {
new Product {
Id =1,
Name = "Dummy1",
Quantity=1
}
}
};
var xmlSerializer = new XmlSerializer(typeof(Order));
var stringBuilder = new StringBuilder();
var xmlTextWriter = XmlTextWriter.Create(stringBuilder,
new XmlWriterSettings { NewLineChars = "\r\n", Indent = true });
xmlSerializer.Serialize(xmlTextWriter, order);
var finalXml = stringBuilder.ToString();
xmlSerializer = new XmlSerializer(typeof(Order));
var xmlReader = XmlReader.Create(new StringReader(finalXml));
var deserializedOrder = (Order)xmlSerializer.Deserialize(xmlReader);
Console.ReadLine();
}
}
[XmlRoot]
public class Order
{
public List<Product> Products { get; set;}
}
Where I would get this XML when I serialized the Order
object I had in play.
="1.0"="utf-16"
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Products>
<Product>
<Id>1</Id>
<Name>Dummy1</Name>
<Quantity>1</Quantity>
</Product>
</Products>
</Order>
And I would get this back when I deserialized the XML back into an Order
object.

All cool so far. However what I then wanted to introduce is a new property on my Order
that held a list in interfaces, something like shown below:
{
static void Main(string[] args)
{
Order order = new Order
{
Products = new List<Product> {
new Product {
Id =1,
Name = "Dummy1",
Quantity=1
}
},
Dispatchers = new List<IDispatcher> {
new FileDispatcher()
}
};
.....
.....
.....
Console.ReadLine();
}
}
[XmlRoot]
public class Order
{
public List<Product> Products { get; set;}
public List<IDispatcher> Dispatchers { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
}
public interface IDispatcher
{
string Channel { get; set; }
void Dispatch();
}
public class FileDispatcher : IDispatcher
{
public string Channel { get; set; }
public void Dispatch()
{
}
}
public class EmailDispatcher : IDispatcher
{
public string Channel { get; set; }
public void Dispatch()
{
}
}
So the code looks fine, it compiles nicely (always a good start). So I then tried to serialize it, and got this:

Mmm not great, so I had a think about this. Ok what about if we take control of the XML Serialization/Deserialization process ourselves. Should be easy enough to do, all we need to do is implement the IXmlSerializable
interface. So let's see what the new code would look like if we went down this path, shall we?
public class Order
{
public List<Product> Products { get; set;}
public ListOfIDispatcher Dispatchers { get; set; }
}
public class ListOfIDispatcher : List<IDispatcher>, IXmlSerializable
{
public ListOfIDispatcher() : base() { }
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Dispatchers");
while (reader.IsStartElement("IDispatcher"))
{
Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
XmlSerializer serial = new XmlSerializer(type);
reader.ReadStartElement("IDispatcher");
this.Add((IDispatcher)serial.Deserialize(reader));
reader.ReadEndElement();
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
foreach (IDispatcher dispatcher in this)
{
writer.WriteStartElement("IDispatcher");
writer.WriteAttributeString("AssemblyQualifiedName",
dispatcher.GetType().AssemblyQualifiedName);
XmlSerializer xmlSerializer = new XmlSerializer(dispatcher.GetType());
xmlSerializer.Serialize(writer, dispatcher);
writer.WriteEndElement();
}
}
}
So I added a new class and altered the Order
class to use my new class. Then, I tried to serialize things again, and now I get this XML:
="1.0"="utf-16"
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Products>
<Product>
<Id>1</Id>
<Name>Dummy1</Name>
<Quantity>1</Quantity>
</Product>
</Products>
<Dispatchers>
<IDispatcher AssemblyQualifiedName="XmlSerialization.FileDispatcher,
XmlSerialization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<FileDispatcher>
<Channel>c:\temp\file001.txt</Channel>
</FileDispatcher>
</IDispatcher>
</Dispatchers>
</Order>
Ah much better, but does it deserialize?…..Mmm well, yes it does, here you go. As I say, it has been a while for me and the XmlSerializer
, I think if you used an abstract
class and registered some known types, somehow that would also work. Anyway, I happy with this and hope it helps someone out there.

For completeness, here is the full listing of everything in its final state:
class Program
{
static void Main(string[] args)
{
Order order = new Order
{
Products = new List<Product> {
new Product {
Id =1,
Name = "Dummy1",
Quantity=1
}
},
Dispatchers = new ListOfIDispatcher {
new FileDispatcher()
{
Channel = @"c:\temp\file001.txt"
}
}
};
var xmlSerializer = new XmlSerializer(typeof(Order));
var stringBuilder = new StringBuilder();
var xmlTextWriter = XmlTextWriter.Create(stringBuilder,
new XmlWriterSettings { NewLineChars = "\r\n", Indent = true });
xmlSerializer.Serialize(xmlTextWriter, order);
var finalXml = stringBuilder.ToString();
xmlSerializer = new XmlSerializer(typeof(Order));
var xmlReader = XmlReader.Create(new StringReader(finalXml));
var deserializedOrder = (Order)xmlSerializer.Deserialize(xmlReader);
Console.ReadLine();
}
}
[XmlRoot]
public class Order
{
public List<Product> Products { get; set;}
public ListOfIDispatcher Dispatchers { get; set; }
}
public class ListOfIDispatcher : List<IDispatcher>, IXmlSerializable
{
public ListOfIDispatcher() : base() { }
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Dispatchers");
while (reader.IsStartElement("IDispatcher"))
{
Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
XmlSerializer serial = new XmlSerializer(type);
reader.ReadStartElement("IDispatcher");
this.Add((IDispatcher)serial.Deserialize(reader));
reader.ReadEndElement();
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
foreach (IDispatcher dispatcher in this)
{
writer.WriteStartElement("IDispatcher");
writer.WriteAttributeString
("AssemblyQualifiedName", dispatcher.GetType().AssemblyQualifiedName);
XmlSerializer xmlSerializer = new XmlSerializer(dispatcher.GetType());
xmlSerializer.Serialize(writer, dispatcher);
writer.WriteEndElement();
}
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
}
public interface IDispatcher
{
string Channel { get; set; }
void Dispatch();
}
public class FileDispatcher : IDispatcher
{
public string Channel { get; set; }
public void Dispatch()
{
}
}
public class EmailDispatcher : IDispatcher
{
public string Channel { get; set; }
public void Dispatch()
{
}
}