Click here to Skip to main content
15,906,574 members
Please Sign up or sign in to vote.
5.00/5 (4 votes)
See more:
Ok I know it's strictly possible. But.....I have a class with say a List<IPerson> where IPerson is an interface. I want to serialize this class to store state.

Is there any simple way apart from implementing IXmlSerializable and doing it all yourself. This seems rather onerous indeed.

Alternatively does anyone have any good example of implementing IXmlSerializable to achieve this?

Not using interfaces is not an option!

What I have tried:

Implementing IXmlSerializable and getting in a complete and utter mess!
Posted
Updated 31-Jan-18 4:48am
Comments
Sergey Alexandrovich Kryukov 7-Jun-16 13:02pm    
Of course you are right, implementation of IXmlSerializeble is lame. The whole idea of such serialization is broken.

Here is the good news: you can forget about all that lame. Next Microsoft attempt to introduce universal, unobtrusive and totally type-agnostic serialization technology is much better than that. Please see my Solution 1.

I up-voted the question, because you raised a critically important problem.

—SA
BillWoodruff 8-Jun-16 10:56am    
Sergey, You can, indeed, serialize an List of "Interface instances" with DataContract ... which is what the OP describes here. If I have time, I'll add an example of doing that to this thread. You can even serialize (the value of) a Static Member of a Static Type !
Sergey Alexandrovich Kryukov 8-Jun-16 11:34am    
You can do... well, anything, but it won't be interface itself, that's all. "Interface instance" is something which runtime type is a class, not interface.
—SA
BillWoodruff 8-Jun-16 12:12pm    
Hi Sergey, in this case I believe the OP does intend to serialize instances of the Interface in their "Interface form," and, I think the assumption that the OP wants to serialize the actual Interface itself is a hypothesis for which there is no reasonable basis. cheers, Bill
Sergey Alexandrovich Kryukov 8-Jun-16 12:21pm    
Then let's say I'm trying to make the terminology more accurate. (All sound hypothesis should be mentioned. :-)
I'll take a look at your solution.
—SA

Somewhen after serialization, comes de-serialization. Since you cannot instantiate an interface, a concrete class (implementing that interface) is required - and now comes the difficult question: what class do you want to use here?
The problem is similar with abstract classes and classes inheriting from them. E.g. Dog and Cat inheriting from (abstract) Animal.
In the end, you can only use concrete classes for serialization / de-serialization. Just imagine a Dog serialized as Animal, and de-serialized as Cat...
 
Share this answer
 
Comments
BillWoodruff 8-Jun-16 10:59am    
You are confusing serializing an interface ... which cannot be done ... with serializing an "instance of an Interface" where you have taken an instance of a Class that inherited form the Interface and cast it into its "Interface form." This most certainly can be done, and is almost certainly what the OP wants to do here.
Let me give you a working example to prove that instances of Interfaces can be serialized. Note that when I say "instances of Interfaces" I mean you have taken valid instances of some Class that inherited from an Interface and cast those instances into their "Interface Form." Naturally, when you save some "interface instance" like this, you do not save the elements of the Class (Fields, Properties) that are not specified in the Interface.

A contrived example demonstrating the techniques involved; to make this "more interesting ... for me", I will also demonstrate how to serialize a static Member of a Static Class (no, you cannot serialize a Static Class itself):
C#
namespace YourNameSpace
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization;

    public interface IAnimal
    {
        Species AnimalSpecies { set; get; }
    }

    public interface ISpecies
    {
        string SpeciesName { set; get; }
    }

    [DataContract]
    public class Species : ISpecies
    {
        [DataMember]
        public string SpeciesName { get; set; }

        public Species(string speciesName)
        {
            SpeciesName = speciesName;
        }
    }

    [DataContract]
    public class Animal : IAnimal
    {
        [DataMember]
        public Species AnimalSpecies { set; get; }

        public Animal(string speciesName)
        {
            AnimalSpecies = new Species(speciesName);
        }
    }

    [DataContract]
    public class Dog : Animal
    {
        [DataMember]
        public string DogName { set; get; }

        public Dog(string dogName, string speciesName) : base(speciesName)
        {
            DogName = dogName;
        }
    }

    [DataContract]
    public class Cat : Animal
    {
        [DataMember]
        public string CatName { set; get; }

        public Cat(string speciesName, string catName) : base(speciesName)
        {
            CatName = catName;
        }
    }

    [DataContract]
    public static class MyAnimals
    {
        [DataMember]
        public static List<Animal> TheAnimals { set; get; }

        [DataMember]
        public static List<IAnimal> TheIAnimals { set; get; }

        static MyAnimals()
        {
            TheAnimals = new List<Animal>();
            TheIAnimals = new List<IAnimal>();
        }
    }

    [DataContract]
    public class MyCats
    {
        [DataMember]
        private List<Cat> MyCatCollection { set; get; }

        public MyCats()
        {
            MyCatCollection = new List<Cat>();
        }

        public void AddCat(Cat theCat)
        {
            MyCatCollection.Add(theCat);
            MyAnimals.TheAnimals.Add(theCat);
            MyAnimals.TheIAnimals.Add(theCat);
        }
    }

    [DataContract]
    public class MyDogs
    {
        [DataMember]
        private List<Dog> MyDogCollection { set; get; }

        public MyDogs()
        {
            MyDogCollection = new List<Dog>();
        }

        public void AddDog(Dog theDog)
        {
            MyDogCollection.Add(theDog);
            MyAnimals.TheAnimals.Add(theDog);
            MyAnimals.TheIAnimals.Add(theDog);
        }
}
Note that every time you add a 'new Dog or Cat Class instance their constructors add the reference to the new Class to two static collections (generic lists) in a static Class named 'MyAnimals. The new Dog or Cat instance is added to a list of the 'Animal Type, and a list of the 'IAnimal Type.

So let's say you create some instances of this Class structure:
// in some Method or EventHandler
MyCats myCats = new MyCats();
myCats.AddCat(new Cat("felix", "HisRoyalMeow"));
myCats.AddCat(new Cat("felix", "HerRoyalMeow"));

MyDogs myDogs = new MyDogs();
myDogs.AddDog(new Dog("Lab", "LikesToBeWet"));
myDogs.AddDog(new Dog("Chihuahua", "Mr. Sausage"));
What you need to do to serialize and de-serialize the List of instances of Cat and Dog stored in their 'IAnimal form in the static Class 'MyAnimals:
C#
public static class SaveTheAnimals
{
    private static DataContractSerializer _dcs;

    public static string filePath = @"C:\Users\YourUserName\Desktop\test\SomeAnimals.xml";

    public static void SerializeMyAnimals()
    {
         // see note below
        _dcs = new DataContractSerializer(typeof(List<IAnimal>), new List<Type>{typeof(Cat), typeof(Dog)});

        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        {
            _dcs.WriteObject(fs, MyAnimals.TheIAnimals);
        }
    }

    public static List<IAnimal> DeSerializeMyAnimals()
    {
        _dcs = new DataContractSerializer(typeof(List<IAnimal>), new List<Type> { typeof(Cat), typeof(Dog) });

        List<IAnimal> myAnimals;

        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            myAnimals = (List<IAnimal>)_dcs.ReadObject(fs);
        }

        return myAnimals;
    }
}
Notes:

1. What makes this "work" is providing the new instance of the DataContract Serializer a list of "known Types" for it to work with. You will note that you can also decorate a DataContract Class with the 'KnownType Attribute for each Type you wish it to be "aware of" ... however, in this case ... a Static Class ... that would not work (don't ask me why) ... and you must provide the list of Known Types to the serializer when you construct it.

2. Look at the actual XML produced by saving the List<IAnimal>
<ArrayOfanyType xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
	<anyType i:type="a:Cat" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>felix</a:SpeciesName>
		</a:AnimalSpecies>
		<a:CatName>HisRoyalMeow</a:CatName>
	</anyType>
	<anyType i:type="a:Cat" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>felix</a:SpeciesName>
		</a:AnimalSpecies>
		<a:CatName>HerRoyalMeow</a:CatName>
	</anyType>
	<anyType i:type="a:Dog" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>LikesToBeWet</a:SpeciesName>
		</a:AnimalSpecies>
		<a:DogName>Lab</a:DogName>
	</anyType>
	<anyType i:type="a:Dog" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>Mr. Sausage</a:SpeciesName>
		</a:AnimalSpecies>
		<a:DogName>Chihuahua</a:DogName>
	</anyType>
</ArrayOfanyType>
Note that each entry contains not only the SpeciesName, which you would expect, but, also, the 'DogName or 'CatName information. And, please ask Microsoft why this works :) because if you cast a specific 'Dog or 'Cat to its IAnimal Form, you should not see that information.
 
Share this answer
 
Have been facing the same problem these days, and I came to this easy, fast and readable solution:

I am using Newtonsoft.JSON to serialize/deserialize objects sent-to/received-from a Web Service. If you want to serialize objects containing Interfaces in their definition, you just need to add a one line settings property.

You just need to add extra information during serialization:
C#
var indented = Formatting.Indented;
var settings = new JsonSerializerSettings()
{
      TypeNameHandling = TypeNameHandling.All
};
string serialized = JsonConvert.SerializeObject(wizardConf, indented, settings);



And then apply the same setting during the deserialization:
C#
var settings = new JsonSerializerSettings()
{
      TypeNameHandling = TypeNameHandling.All
};
YourObject obj =  JsonConvert.DeserializeObject<yourobject>(JsonString, settings);
</yourobject>



So, let's say you got to serialize an object containing two list of Interfaces:
C#
public class Class1 : IInterface1
{
   public void method1()
   {
       // whatever1
   }
}

public class Class2 : IInterface2
{
   public void method2()
   {
       // whatever2
   }
}

   public class ObjToSerialize
   {
       public List<IInterface1> list1;
       public List<IInterface2> list2;

       public ObjToSerialize()
       {
           list1 = new List<IInterface1>();
           list2 = new List<IInterface2>();
       }
   }




Let's say that at runtime you added one object each:
C#
ObjToSerialize obj = new ObjToSerialize();
obj.list1.Add(new Class1());
obj.list2.Add(new Class2());



when it comes to serializing it, just do as described above, and the result string will appears as follows:
XML
{
  "$type": "Namespace.ObjToSerialize, AssemblyName",
  "list1": {
    "$type": "System.Collections.Generic.List`1[[Namespace.IInterface1, AssemblyName]], mscorlib",
    "$values": [
      {
        "$type": "Namespace.Class1, AssemblyName"
      }
    ]
  },
  "list2": {
    "$type": "System.Collections.Generic.List`1[[Namespace.IInterface2, AssemblyName]], mscorlib",
    "$values": [
      {
        "$type": "Namespace.Class2, AssemblyName"
      }
    ]
  }
}



The easily readable JSON file basically tells everything you need to know.
It says that the ObjToSerialize contains two properties, each of them is a List of Interfaces, whose implementation is Class1 in the first case and Class2 in the second case.

The deserializer above will take this string and easily reconstruct the object as it has all the details of the interfaces implementation.
 
Share this answer
 
Comments
Dave Black 30-Nov-23 13:33pm    
The OP is talking about Xml Serialization NOT Json Serialization!
Just read this article I wrote about serialization of interfaces:
Reading and Writing XML in C#/VB.Net[^]
 
Share this answer
 
Comments
CHill60 1-Feb-18 8:14am    
The question is over a year old and already has an accepted answer. Posting links to your own articles is often perceived as rep-point "farming" and can lead to downvoting
Dirk Bahle 1-Feb-18 16:30pm    
Did you actually notice that the article reference is relevant?

So, I can only write an article OR answer Q&A but not both, is that right?
CHill60 1-Feb-18 16:55pm    
Yes, I did. Which is why I only commented to let you know about perceptions. That (or those) downvote(s) are nothing to do with me.
But, the general advice would be - stick to answering new posts
Dirk Bahle 1-Feb-18 11:34am    
Oh OK, I saw it listed in the front page and thought the question was current - rep-point farming is a funny term :-) thats mot my intention but we can include any other link to any other article if you happen to know one that answers this particular question - I really don't know one which is why I wrote the article in the first place :-)
Please learn the best and easiest to use serialization approach in .NET, Data Contract: Using Data Contracts.

Note, the notion of serialization of interface makes no sense. Interfaced don't contain data (only metadata).

—SA
 
Share this answer
 
Comments
BillWoodruff 8-Jun-16 11:41am    
Sergey, I welcome you review of the solution I posted here, and my comments on your comment to the OP. collegially yours, Bill

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900