Martin Braun
Martin Braun

Reputation: 12649

How to serialize a list of an interface?

I got an object that has to inherit from a general interface as well as it has a list from that interface as property.

Going to serialize it follows by problems, as the XmlSerialzer is not able to determine the real types that are inside of my MyClass.Items list elements.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace MyTest
{
    public interface IMyInterface
    {
        string TestProperty { get; set; }
    }
    public class MyClass : IMyInterface
    {
        public string TestProperty { get; set; }
        public List<IMyInterface> Items { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            MyClass obj = new MyClass() { TestProperty = "Test" };
            XmlSerializer xs = new XmlSerializer(typeof(MyClass)); // <- throws System.NotSupportedException
            using (XmlWriter xw = XmlWriter.Create("file.xml", new XmlWriterSettings()
            {
                Encoding = Encoding.UTF8,
                Indent = true,
                NewLineHandling = NewLineHandling.Entitize
            }))
            {
                xs.Serialize(xw, obj);
            }
        }
    }
}

How can I serialize a List<T> where T is an interface?

Upvotes: 2

Views: 1947

Answers (2)

Martin Braun
Martin Braun

Reputation: 12649

So, based on decPLs answer I've created a class to do this job. T must be an interface and it will work:

public class InterfaceCollection<T> : Collection<T>, IXmlSerializable where T : class
{
    private string Namespace { get; set; }
    private string Assembly { get; set; }

    public InterfaceCollection()
    {
    }

    public InterfaceCollection(IList<T> list, string namespaceOfInheritedTypes = null, string assemblyOfInheritedTypes = null)
        : base(list)
    {
        this.Namespace = namespaceOfInheritedTypes ?? null;
        this.Assembly = assemblyOfInheritedTypes ?? null;
    }

    public InterfaceCollection(string namespaceOfInheritedTypes, string assemblyOfInheritedTypes = null)
    {
        this.Namespace = namespaceOfInheritedTypes ?? null;
        this.Assembly = assemblyOfInheritedTypes ?? null;
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        this.Namespace = reader.GetAttribute("fromNamespace");
        this.Assembly = reader.GetAttribute("fromAssembly");

        reader.MoveToContent();
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                Type type;
                if (this.Assembly != null)
                {
                    type = Type.GetType(this.Namespace + "." + reader.Name + ", " + this.Assembly);
                }
                else
                {
                    type = Type.GetType(this.Namespace + "." + reader.Name);
                }
                if (type != null)
                {
                    XmlSerializer xs = XmlSerializer.FromTypes(new[] { type })[0];
                    this.Items.Add((T)xs.Deserialize(reader));
                }
            }
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("fromNamespace", this.Namespace);
        if (this.Assembly != null) writer.WriteAttributeString("fromAssembly", this.Assembly);
        foreach (T element in this)
        {
            Type type = element.GetType();
            XmlSerializer xs = XmlSerializer.FromTypes(new[] { type })[0];
            xs.Serialize(writer, element);
        }
    }
}

Upvotes: 1

decPL
decPL

Reputation: 5402

Unfortunately it's not supported out of the box, but it's doable. You would have to use the IXmlSerializable interface and write your custom serialization. It shouldn't be that much difficult - you'd need to enumerate through your list, get the underlying type of each object and create a new XmlSerializer for this type. Deserialization could be a bit more tricky, as you'd need to parse the class names to determine runtime types.

Upvotes: 1

Related Questions