Click here to Skip to main content
15,868,093 members
Articles / Programming Languages / XML

XmlSerializer: Serializing List of Interfaces

Rate me:
Please Sign up or sign in to vote.
4.70/5 (9 votes)
3 Mar 2014CPOL1 min read 36.7K   7   9
XmlSerializer: Serializing list of interfaces

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:

C#
class Program
{
    static void Main(string[] args)
    {
        Order order = new Order
        {
            Products = new List<Product> {
                new Product {
                    Id =1,
                    Name = "Dummy1",
                    Quantity=1
                }
            }
        };

        //serialize
        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();

        //deserialize
        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.

XML
<?xml version="1.0" encoding="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.

image

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:

C#
{
    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()
    {
        //This would do something in real code
    }
}

public class EmailDispatcher : IDispatcher
{
    public string Channel { get; set; }

    public void Dispatch()
    {
        //This would do something in real code
    }
}

So the code looks fine, it compiles nicely (always a good start). So I then tried to serialize it, and got this:

image

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?

C#
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:

XML
<?xml version="1.0" encoding="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.

image

For completeness, here is the full listing of everything in its final state:

C#
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"
                }
            }
        };

        //serialize
        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();

        //deserialize
        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()
    {
        //This would do something in real code
    }
}

public class EmailDispatcher : IDispatcher
{
    public string Channel { get; set; }

    public void Dispatch()
    {
        //This would do something in real code
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
mishrsud18-Nov-15 12:24
professionalmishrsud18-Nov-15 12:24 
GeneralRe: My vote of 5 Pin
Sacha Barber18-Nov-15 19:50
Sacha Barber18-Nov-15 19:50 
QuestionGood stuff. Just a thought Pin
Michael Breeden16-Nov-15 9:20
Michael Breeden16-Nov-15 9:20 
I needed to a small detail of doing something with XML and I noticed that your code had the detail. I was going to move on, but I thought I would check for if there was anything else I should note. ... Looking through the code, I didn't see much of interest, then I saw your list of coding recognitions and thought "WTF", this guy has done good stuff. There has to be more I can learn here. Examining more closely, it is interesting, but my thought was that it could use more explanation, partly why did you do what you did... I mean I understood the code and even its usage, but I thought maybe you ought to put some more explanation of why even more than how... Just a thought.

AnswerRe: Good stuff. Just a thought Pin
Sacha Barber16-Nov-15 20:34
Sacha Barber16-Nov-15 20:34 
SuggestionGeneric approch Pin
neyerMat29-Oct-15 2:29
neyerMat29-Oct-15 2:29 
QuestionVote of 5 Pin
Kenneth Haugland27-Jan-15 17:04
mvaKenneth Haugland27-Jan-15 17:04 
AnswerRe: Vote of 5 Pin
Sacha Barber27-Jan-15 21:45
Sacha Barber27-Jan-15 21:45 
GeneralNice Pin
Niel M.Thomas3-Mar-14 19:40
professionalNiel M.Thomas3-Mar-14 19:40 
GeneralRe: Nice Pin
Sacha Barber3-Mar-14 19:58
Sacha Barber3-Mar-14 19:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.