Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

XmlSerializer : Serializing list of interfaces

, 3 Mar 2014
Rate this:
Please Sign up or sign in to vote.
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 Where I would get this Xml when I serialized the Order object I had in play. And I would […]

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

        //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 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 to introduce 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()
    {
        //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 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 it implement the IXmlSerializable interface. So lets 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 an altered the Order class to use my new class. Then I tried to serialize things again, and now I get this 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 have been a while for me an 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

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)

Share

About the Author

Sacha Barber
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 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

 
GeneralNice PinprofessionalNiel M.Thomas3-Mar-14 19:40 
GeneralRe: Nice PinmvpSacha Barber3-Mar-14 19:58 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 3 Mar 2014
Article Copyright 2014 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid