2

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?

Martin Braun
  • 10,906
  • 9
  • 64
  • 105
  • Slightly off the topic, have you consider having two classes: `MyClass : IMyInterface` and `MyClassList : List`. Purely to separate responsibilities? – oleksii Apr 11 '14 at 08:42
  • Yes, but 1. that leads to the same problem and 2. `MyClass` would be `MyClassList` in the context of my scope. – Martin Braun Apr 11 '14 at 08:47
  • Question is: why do you want to [inherit from list](http://stackoverflow.com/q/5376203/1997232)? Not sure if you run into the same problem with `List<>` property though. If you do, then at least you can use base class instead of interface now. If base class can serialize self, then you can surely serialize array of base classes. – Sinatr Apr 11 '14 at 10:12

2 Answers2

1

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.

decPL
  • 5,384
  • 1
  • 26
  • 36
1

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);
        }
    }
}
Martin Braun
  • 10,906
  • 9
  • 64
  • 105