2

I'm getting a "type is interface, cannot be instanciated" deserialization error with json.net even though I am specifying type on the object I'm trying to deserialize

private static JsonSerializerSettings settings = new JsonSerializerSettings { 
    TypeNameHandling = TypeNameHandling.Auto };

/// <summary>
/// Returns an object created from the jObject and placed in a stub object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jObj"></param>
/// <returns></returns>
public static T FromJObject<T>(JToken jObj)
{
    if (jObj == null)
    {
        jObj = new JObject();
    }
    if (jObj is JValue)
    {
        return (T)((JValue)jObj).Value;
    }
    else
    {
        return (T)JsonConvert.DeserializeObject<T>(jObj.ToString(), settings);
    }
 }

this is jObj

{
  "Id": 2,
  "Name": "Name",
  "JsonMapEnum": 0,
  "Validations": [
    {
      "Id": 1,
      "$type": "JsonMap.Default.JValidation, JsonMap"
    }
  ],
  "JSType": 3,
  "SubJsonMapEnum": -1,
  "$type": "JsonMap.Default.JAttribute, JsonMap"
}

This is the error

Could not create an instance of type JsonMap.Interfaces.IValidation. Type is an interface or abstract class and cannot be instantated. Path 'Validations[0].Id'

It looks like it's trying to turn Id into a Validation object. Why?

these are the interfaces implemented by my types

public interface IJsonMap
{
    long Id { get; set; }
    String Name { get; set; }
    LazyEnum JsonMapEnum { get; set; }
}

public interface IAttribute : IJsonMap
{
    IEnumerable<IValidation> Validations { get; set; }
    LazyEnum JSType { get; set; }
    LazyEnum SubJsonMapEnum { get; set; }
}

public interface IValidation : IJsonMap
{
    IEnumerable<IArgument> Arguments { get; set; }
}

this is the call

FromJObject<JAttribute>(CreationJObj)

JAttribute implements IAttribute

2 Answers 2

2

Apparently, "$type" has to be the first property in the String literal in order for the type to be caught by the type name handler. I imagine this is because The deserializer isn't checking for the existence of $type, it simply uses the json string reader, and upon finding the first property without the type being set, it fails out.

Here's the method I created to ensure $type is always first

    private static JToken ReorderJToken(this JToken jTok)
    {
        if (jTok is JArray)
        {
            var jArr = new JArray();
            foreach (var token in jTok as JArray)
            {
                jArr.Add(token.ReorderJToken());
            }
            return jArr;
        }
        else if( jTok is JObject)
        {
            var jObj = new JObject();
            foreach(var prop in (jTok as JObject).Properties().OrderBy(x=> x.Name))
            {
                prop.Value = prop.Value.ReorderJToken();
                jObj.Add(prop);
            }
            return jObj;
        }
        return jTok;
    }
Sign up to request clarification or add additional context in comments.

3 Comments

Apparently, "$type" has to be the first property. No need for it. Suppose you are the author of the serializer, would you use dict["$type"] or list[0]
Fine, but have a method that reorganizes you're properties , I had to create a utility method for just that reason, and it was not obvious in any way that this was a requirement. When dealing with browsers and client side json creation, this doesn't seem like an out of left field use case.
Don't get me wrong, I am eternally grateful to JamesNK for creating the tool, all I'm saying is that this is unexpected behavior, sorry for letting my frustration make me seem smarmy.
0

Well clearly it cannot infer the proper type based off the arguments you're feeding it. Without more context of how it's called I can't make what you have work. However, if you call it with an object cast to the interfaces type it will not work. With the definition you have FromObject must always be called with T is the type of a class. First thing to do is make sure you never try to call the method where T is equal to typeof(IOneOfMyInterfaces) because this is guaranteed to fail. If that doesn't fix it I would try calling a different version of the deserialize method;

IMyInterface obj = (IMyInterface)serializer.Deserialize(validatingReader, t);

This works where validatingReader is a JsonReader and t is the Type of the class object implementing IMyInterface. I'm currently using it for generic deserialization with json schemas for validating json correctness.

1 Comment

oops, sorry, forgot to include that. JTransformer.FromJObject<JAttribute>(CreationJObj)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.