views:

509

answers:

4

I have a class definition that contains a property that returns an interface.

public class Foo
{ 
    public int Number { get; set; }

    public ISomething { get; set; }
}

Attempting to serialize the Foo class using Json.NET gives me an error message like, "Could not create an instance of type 'ISomething'. ISomething may be an interface or abstract class."

Is there a Json.NET attribute or converter that would let me specify a concrete Something class to use during deserialization?

A: 

Here is a reference to an article written by ScottGu

Based on that, I wrote some code which I think might be helpful

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

    }

    public class School : IEducationalInstitute
    {
        private string name;
        #region IEducationalInstitute Members

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        #endregion
    }

    public class Student 
    {
        public IEducationalInstitute LocalSchool
        {
            get; set;
        }

        public int ID
        {
            get; set;
        }
    }

    public static class JSONHelper
    {
        public static string ToJSON(this object obj)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(obj);
        }
        public  static string ToJSON(this object obj, int depth)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RecursionLimit = depth;
            return serializer.Serialize(obj);
        }
    }

And this is how you would call it

School myFavSchool = new School()
                                    {
                                        Name = "JFK High School"
                                    };
            Student sam = new Student()
                            {
                                ID = 1,
                                LocalSchool = myFavSchool
                            };
        string jSONstring = sam.ToJSON();

        Console.WriteLine(jSONstring);
//Result {"LocalSchool":{"Name":"JFK High School"},"ID":1}

If I understand it correctly, I do not think you need to specify a concrete class which implements the interface for JSON serialization.

ram
Your sample uses the JavaScriptSerializer, a class in the .NET Framework. I'm using Json.NET as my serializer. http://www.codeplex.com/Json
dthrasher
A: 

I've wondered this same thing, but I'm afraid it can't be done.

Let's look at it this way. You hand to JSon.net a string of data, and a type to deserialize into. What is JSON.net to do when it hit's that ISomething? It can't create a new type of ISomething because ISomething is not an object. It also can't create an object that implements ISomething, since it doesn't have a clue which of the many objects that may inherit ISomething it should use. Interfaces, are something that can be automatically serialized, but not automatically deserialized.

What I would do would be to look at replacing ISomething with a base class. Using that you might be able to get the effect you are looking for.

Timothy Baldridge
I realize it won't work "out of the box". But I was wondering if there were some attribute like "[JsonProperty(typeof(SomethingBase))]" that I could use in order to provide a concrete class.
dthrasher
So why not use SomethingBase instead of ISomething in the above code?It could be argued that we are also looking at this the wrong way as Interfaces shouldn't be used in serialization, since they simply define communication "interface" with a given class. Serializing a interface technically is nonsense, as is serializing an abstract class. So while it "could be done" I'd argue that it "shouldn't be done".
Timothy Baldridge
Have you looked at any of the classes in the Newtonsoft.Json.Serialization Namespace? particularly the JsonObjectContract class?
johnny
+3  A: 

One of the things you can do is:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;

JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

The TypeNameHandling flag will add a _type property to the JSON, which allows Json.NET to know which concrete type it needs to deserialize the object into. This allows you to deserialize an object while still fulfilling an interface or abstract base class.

The downside, however, is that this is very Json.NET-specific. The _type will be a fully-qualified type, so if you're serializing it with type info,, the deserializer needs to be able to understand it as well.

Daniel T.
Interesting. I'll have to play around with this. Nice tip!
dthrasher