14

I'm trying to run code similar to this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    [Serializable]
    [XmlInclude(typeof(List<Class2>))]
    public class Class1
    {
        private IList<Class2> myArray;

        public IList<Class2> MyArray
        {
            get { return myArray; }
            set { myArray = value; }
        }

    }

    public class Class2
    {
        private int myVar;

        public int MyProperty
        {
            get { return myVar; }
            set { myVar = value; }
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            XmlSerializer ser = new XmlSerializer(typeof(Class1), new Type[] { typeof(List<Class2>) });
            FileStream stream = File.OpenWrite("Data.xml");
            ser.Serialize(stream, new List<Class1>());
            stream.Close();
        }
    }
}

Can somebody explain to me what am I doing wrong?

I get a:

Cannot serialize member .. MyArray ... because it is an interface.

Shouldn't the XmlInclude resolve this?

Crab Bucket
  • 6,219
  • 8
  • 38
  • 73
Shahar
  • 161
  • 1
  • 1
  • 4

4 Answers4

18

No. You can't serialize an interface. Ever. It just told you that.

An interface is nothing more than a description of a set of behaviors. It says nothing about the contents of an instance. In particular, although an instance of a class implementing an interface must implement all of its members, it will certainly have properties of its own which need to be serialized.

How would it be deserialized?

What class would be used to deserialize the interface on the other side?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • 3
    @Shahar: yes. Don't _do_ that. Use a concrete type, not an interface. – John Saunders Jan 11 '11 at 15:33
  • 2
    How would it be deserialized? As an instance of the class that was serialized, just the same as List. What class would be used to deserialize the interface on the other side? See first answer. – jwg Mar 22 '13 at 15:13
  • But how would the other side know which class was serialized? Would it even _know_ which class? – John Saunders Mar 22 '13 at 15:19
  • 1
    @John Saunder I would strongly disagree with your statement. Take for example a classic case of interface IShape and Rectangle, Circle implementing it, and MyObject which has a reference to IShape. Now what if I need to have MyObject serializable? "Don't do it" simple does not make any sense, as my class structure must follow some general design principles, which also involves using interfaces. I dont want not have bad design just because I can't serialize interfaces and statement like 'don't do that', as serialization comes second after good design, IMO. – Califf Mar 22 '13 at 20:35
  • If your application requires serialization, then designing your application to require classes which can be serialized would not be good design. If you require serialization, then you need to design, for instance, a "persistence layer" which includes classes that can be serialized. The main part of your application can continue to use references to interfaces. Just translate to and from the "persistence" flavor when necessary to persist (serialize). – John Saunders Mar 22 '13 at 20:54
  • Besides, I never said "don't do it". I said "you _cannot_ do it". – John Saunders Mar 22 '13 at 20:55
  • 24
    @JohnSaunders, your first paragraph was right when you said "you cannot do it". Everything else you said was trying to justify why this should be the case. These justifications are bogus. Take an interface, and replace it by an abstract class without members. This doesn't change how the code works at all. Suddenly you can serialize it! – jwg Mar 25 '13 at 10:14
  • 4
    Also, you did say "Don't do that", in your comment above. – jwg Mar 25 '13 at 10:15
17

Here's an untested shady workaround that i tend to use in principle:

private IList<Class2> myArray;
[XmlIgnore]
public IList<Class2> MyArray
{
    get { return myArray; }
    set { myArray = value; }
}

[XmlElement("MyArray")]
public object MyArraySerializable
{
    get { return MyArray; }
    set { MyArray = value as IList<Class2>; }
}

This will serialize whatever list you may be using as generic object with a type attribute which will tell the deserializer the actual type of the object, so when that object gets deserialized it should be cast to a IList<Class2> again. Remember to provide any types that may be assumed by the interface.


I see no reason why any serializer should be unable to serialize such properties. It is not like you actually try to serialize an interface, you try to serialize an object which implements a certain interface (which is not far different from abstract subclassing, some programming languages even solely operate on interfaces).

When the serializer should serialize that object it knows that the object implements that interface, all it really has to do is serialize it and attach the type attribute (like it does if you serialize abstract classes or just super-classes in general).

Now the deserializer looks at the type and can check if that object in fact implements the required interface and then deserialize it to the respective property.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • 2
    Amen. The serializer is stupid when it comes to this! Thanks for the work-around. It is much better to use interfaces because inheritance is limited to one base class. – toddmo Nov 06 '13 at 16:24
11

Or use the DataContractSerializer instead.

joniba
  • 3,339
  • 4
  • 35
  • 49
  • 1
    This is indeed the correct answer. http://blogs.msdn.com/b/sowmy/archive/2008/10/04/serializing-internal-types-using-xmlserializer.aspx – l33t May 29 '13 at 07:38
0

You included typeof(List<...>), but MyArray is of type IList<...> which is no apparent data structure itself but more of a placeholder to take some data structure.

Change the type of MyArray to a specific type (as List, e.g.) and it should work.

    private List<Class2> myArray;

    public List<Class2> MyArray
    {
        get { return myArray; }
        set { myArray = value; }
    }
Martin Hennings
  • 16,418
  • 9
  • 48
  • 68