How to serialize interface types and lists using the XmlSerializer

The XmlSerializer works fine as long as you work with concrete classes. But soon as you specify an interface within an object the serialization will fail with the message “Cannot serialize member …” But there’s a solution to hack this.

Have a look at the following example classes.

public interface ISerializedItem
{
    string Name { get; set; }
}

public class ItemA : ISerializedItem { ... }
public class ItemB : ISerializedItem { ... }

public class ToSerialize
{
    public List<ISerializedItem> Items { get; set; }
}

When you’re trying to serialize such objects, you will get an exception. The workaround is to implement the IXmlSerializable interface.

There’re three methods to implement:

  • public XmlSchema GetSchema()
  • public void ReadXml(XmlReader reader)
  • public void WriteXml(XmlWriter writer)

The GetSchema can return NULL – it’s not used in our case. The ReadXml and WriteXml Method must implement the serialization. As long this is not trivial here’s the complete code for the example above.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Xml.XPath;


namespace GenericInterfaceXmlSerializer
{
    public interface ISerializedItem
    {
        string Name { get; set; }
    }
    
    public class ItemA : ISerializedItem
    {
        public string Name { get; set; }
    }
    
    public class ItemB : ISerializedItem
    {
        public string Name { get; set; }
    }
    
    public class ToSerialize : IXmlSerializable
    {
        public List<ISerializedItem> Items { get; set; }
        
        public ToSerialize()
        {
            Items = new List<ISerializedItem>();
        }
        
        #region IXmlSerializable implementation
        
        public XmlSchema GetSchema()
        {
            return null;
        }
        
        public void ReadXml(XmlReader reader)
        {
            // Create a XPathDocument out of the Xml in order to navigate through
            XPathDocument xPath = new XPathDocument(reader);
            XPathNavigator navigator = xPath.CreateNavigator();
            
            // Iterate through all elements of Items
            XPathNodeIterator iterator = navigator.Select("//ToSerialize/Items/Element");
            while (iterator.MoveNext())
            {
                Dictionary<string, string> values = ReadNodes(iterator.Current);
                
                // Create element of the specified type
                ISerializedItem element = (ISerializedItem)Activator.CreateInstance(Type.GetType(values["Type"]));
                element.Name = values["Name"];
                
                Items.Add(element);
            }
        }
        
        public void WriteXml(XmlWriter writer)
        {
            // Create a new Xml Document
            XmlDocument xdoc = new XmlDocument();
            
            // Iterate trough all Items and append them
            XmlNode items = xdoc.AppendChild(CreateNode(xdoc, "Items", string.Empty));
            foreach (ISerializedItem item in Items)
            {
                XmlNode element = items.AppendChild(CreateNode(xdoc, "Element", string.Empty));
                element.AppendChild(CreateNode(xdoc, "Type", item.GetType().FullName));
                element.AppendChild(CreateNode(xdoc, "Name", item.Name));
            }
            
            // Output XML
            xdoc.WriteTo(writer);
        }
        
        private static XmlNode CreateNode(XmlDocument doc, string name, string value)
        {
            XmlNode node = doc.CreateNode(XmlNodeType.Element, name, string.Empty);
            if (!string.IsNullOrEmpty(value))
            node.AppendChild(doc.CreateTextNode(value));
            return node;
        }
        
        private static Dictionary<string, string> ReadNodes(XPathNavigator navigator)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            if (!navigator.MoveToFirstChild())
            return result;
            
            do { result.Add(navigator.Name, navigator.Value); } while (navigator.MoveToNext());
            
            navigator.MoveToParent();
            return result;
        }
        
        #endregion
    }
    
    class Program
    {
        /// <summary>
        /// Example Code
        /// </summary>
        static void Main()
        {
            try
            {
                ToSerialize toSerialize = new ToSerialize();
                toSerialize.Items.Add(new ItemA{Name="Cat"});
                toSerialize.Items.Add(new ItemB{Name="Dog"});
                
                XmlSerializer serializer = new XmlSerializer(typeof(ToSerialize));
                XmlTextWriter writer = new XmlTextWriter("test.xml", Encoding.UTF8);
                serializer.Serialize(writer, toSerialize);
                writer.Close();
                
                XmlTextReader reader = new XmlTextReader("test.xml");
                ToSerialize result = (ToSerialize) serializer.Deserialize(reader);
                reader.Close();
                
                Debug.Assert(toSerialize.Items.Count == result.Items.Count, "Count does not match");
            }
            catch (Exception exc)
            {
                do
                {
                    Console.Error.WriteLine(exc.Message);
                    exc = exc.InnerException;
                } while (exc != null);
            }
        }
    }
}

The trick is, to serialize also the type of the element within the collection. As you can see the ReadXml method first reads the Type of the element and creates an object of that type by using the Activator.

ISerializedItem element = (ISerializedItem)Activator.CreateInstance(Type.GetType(values["Type"]));

After that the interface properties can be set with the values of the de-serialization.

Pretty cool I think.
Cheers
- Gerhard

kick it on DotNetKicks.com

Using Async Delegates instead of the Thread class

If you want to write some asnychronous code, the good old fashioned way is to create a new Thread. But this has several pitfalls, e.g. when you need to use parameters or you need to retrieve a return value. Then this way is getting tricky.

Have a look at this solution that is using async delegates instead of the Thread class.

private delegate bool ThreadDelegate(int counter, int sleep);

/// <summary>
/// Thread Method.
/// </summary>
public static bool ThreadMethod(int counter, int sleep)
{
    for (int x = counter; x > 0; x--)
    {
        Console.WriteLine("Sleeping " + sleep + "ms");
        Thread.Sleep(sleep);
    }
    
    return true;
}

/// <summary>
/// Main Thread
/// </summary>
static void Main()
{
    ThreadDelegate threadDelegate = ThreadMethod;
    
    IAsyncResult result = threadDelegate.BeginInvoke(10, 200, null, null);
    // do something between BeginInvoke and EndInvoke
    bool threadResult = threadDelegate.EndInvoke(result);
    
    Console.WriteLine("Result " + threadResult);
}

And now the opposite, the solution that is using the Thread class.

/// <summary>
/// Thread Arguments
/// </summary>
class ThreadArguments
{
    public int counter;
    public int sleep;
    
    public bool result;
}

/// <summary>
/// Thread method
/// </summary>
public static void ThreadMethod(object argument)
{
    ThreadArguments ta = (ThreadArguments) argument;
    for (int x = ta.counter; x > 0; x--)
    {
        Console.WriteLine("Sleeping " + ta.sleep + "ms");
        Thread.Sleep(ta.sleep);
    }
    
    ta.result = true;
}

/// <summary>
/// Main Thread
/// </summary>
static void Main()
{
    ThreadArguments ta = new ThreadArguments();
    ta.counter = 10;
    ta.sleep = 200;
    
    Thread thread = new Thread(ThreadMethod);
    thread.Start(ta);
    thread.Join();
    
    Console.WriteLine("Result " + ta.result);
}

As you can see, that’s not really a nice solution to do such things, because you have to create a separated class to pass the parameters to the thread function. I think the better solution is the first one that is using async delegates.

I hope that I could deliver you some new insights about async delegates and threads.
- Gerhard

kick it on DotNetKicks.com