2

I'm using Entity Framework Code First table-per-hierarchy in my ASP.NET Web API project. One of my models has a List that is of the type of the abstract base class of the hierarchy.

List<Dog> // (for example; with GoldenRetriever, BorderCollie, etc inheriting)

I'm trying to test POSTing some data to my API Controller using Fiddler. But I don't know how to represent the JSON when I do so. If I try something like:

"Dogs":
[
    {"Name":"Bud", "Age":"3"}
]

I get the error:

"Could not create an instance of type Models.Dog. Type is an interface or abstract class and cannot be instantiated."

Specifying the Discriminator in the JSON doesn't help me either. Anyone have any ideas? Thanks!

Edit: Solution

The trick is to use the $type property in the JSON string. For more information see this link suggested by m.t.bennett in the comments.

To enable using the $type property I needed to add the following to WebApiConfig.cs:

config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling 
    = TypeNameHandling.Auto;

Then when posting the JSON to Fiddler, I added the $type property with the full object path:

{"$type":"Example.Models.Dogs.GoldenRetriever, Example.Models",
    "Name":"Bud","Age":3}

For me to figure out this formatting I used Snixtor's suggestion to serialize an object and output the JSON string. Brilliant!

I'm not sure if this is the most elegant solution since it's JSON.NET specific, but it works!

Community
  • 1
  • 1
brudert
  • 537
  • 8
  • 21
  • 2
    I find the best way to determine JSON formatting for a model is to serialize an existing instance *to* JSON. Have you tried that? – Snixtor Apr 25 '13 at 22:42
  • @Snixtor Great advice (and will use this in the future), but it didn't help. I used JsonConvert to serialize the list and it spat out basically what I was trying already. There was no evidence of any way to discriminate between object types. – brudert Apr 25 '13 at 23:06
  • 1
    does this link http://stackoverflow.com/questions/6348215/how-to-deserialize-json-into-ienumerablebasetype-with-newtonsoft-json-net help at all? – m.t.bennett Apr 25 '13 at 23:20
  • @m.t.bennett That did help! Using your suggestion and Snixtor's we solved the riddle. I'll update my answer with the solution shortly. – brudert Apr 25 '13 at 23:49

1 Answers1

0

I used a custom JsonConverter to handle the base-type deserialization.

    public override bool CanConvert(Type objectType) {
        return typeof(Mybase).Equals(objectType);
    }

    public override MyBase Deserialize(JsonReader reader, MyBase existingValue, JsonSerializer serializer) {
        var ret = existingValue;
        var jobj = JObject.ReadFrom(reader);
        if (jobj["type"] == null || jobj["type"].Type != JTokenType.String)
            throw new JsonSerializationException("Supplied JSON is missing the required 'type' member.");

        var typeName = jobj["type"].Value<string>();

        var t = this.GetType().Assembly.GetType("MyNamespace." + typeName);
        if(t == null)
            throw new JsonSerializationException(String.Format("Could not identify supplied type '{0}' as a known type.", typeName));

        if (existingValue != null && !t.IsInstanceOfType(existingValue))
            throw new JsonSerializationException(String.Format("Type Mismatch: existingValue {0} is not assignable from supplied Type {1}", existingValue.GetType().Name, t.Name));

        ret = (ContactMethod)jobj.ToObject(t);

        return ret;
    }

And registered the JsonConverter durring application initialization:

JsonSerializerSettings settings;
settings.Converters.Add(new MyBaseConverter());
Eric Falsken
  • 4,796
  • 3
  • 28
  • 46