Havivi Havivi - 3 months ago 29
ASP.NET (C#) Question

Failing to deserialize json into abstract class in ASP.Net MVC

I know there are lot of questions and answers about this issue, but none helped me.

I have 2 classes that inherits from one abstract base class. I'm trying to write POST method in my controller that gets the base class as parameter, but I got exception:


MissingMethodException: Cannot create an abstract class.


I've found solution in this answer: Deserialising Json to derived types in Asp.Net Web API, but it didn't helped.

I liked the idea and followed the answer there to the end, but still, got the same exception and my code was never called.

To isolate the problem, I've started new ASP.NET project (SPA template) and checked my problem on clean project. My implementation:

My JsonConverter implementation is the same as the one form the answer:

public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// this is very important, otherwise serialization breaks!
/// </summary>
public override bool CanWrite => false;

/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">contents of JSON object that will be
/// deserialized</param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);

public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}

public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
//////////////////////////////////////////
// This function never get called!!!!!!!!!
//////////////////////////////////////////

if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
JObject jObject = JObject.Load(reader);

// Create target object based on JObject
T target = Create(objectType, jObject);

// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);

return target;
}

public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}


My base class and it's implementation is:

[JsonConverter(typeof(MyCustomConverter))]
public abstract class BaseClass
{
private class MyCustomConverter : JsonCreationConverter<BaseClass>
{
protected override BaseClass Create(Type objectType,
Newtonsoft.Json.Linq.JObject jObject)
{
//////////////////////////////////////////
// No matter what I put in here.
// This function never get called!!!!!!!!!
//////////////////////////////////////////
return new Sub1()
{
Name = "aaaa",
Prop1 = 233
};
}
}

public int Prop1 { get; set; }
}

public class Sub1 : BaseClass
{
public string Name { get; set; }
}


My controller Post and Get methods are:

// GET: /Account/GetTest
[HttpGet]
public JsonResult GetTest()
{
// This function works as expected.
Sub1 sub1 = new Sub1
{
Name = "SomeName",
Prop1 = 5
};
return Json(sub1, JsonRequestBehavior.AllowGet);
}

// Post: /Account/PostTest
[HttpPost]
public JsonResult PostTest(BaseClass res)
{
// I never get to this point.
return Json(res, JsonRequestBehavior.AllowGet);
}


And my JS test code that tries to use this APIs is:

$.ajax({
url: '/Account/GetTest',
method: 'get',
cache: false,
success: function(data) {
// data arrive fine to this point

$.ajax({
url: '/Account/PostTest',
method: 'post',
cache: false,
data: data,
// I've also tried: data: JSON.stringify(data),
success: function () {
// I never get success...
alert('Success');
},
error: function () {
alert('Error Post');
}
});

},
error: function() {
alert('Error Get');
}
});


I've also tried to play with the ajax parameters, but without any luck.

Does someone know what am I doing wrong?

Answer

After many experiments, I've found that using JsonConverter attribute on the base class works only in ApiController, and not in regular MVC Controller. I've just created new ApiController and it worked like a charm.

Plus, whens posting the json, it should contain contentType information, so it should look like:

$.ajax({
        url: '/Account/PostTest',
        method: 'post',
        cache: false,
        //////// NOTE THE NEXT 2 ATTRIBUTES
        data: JSON.stringify(data),
        contentType: 'application/json; charset=utf-8'
        /////////////////
        success: function () {
            // I never get success...
            alert('Success');
        },
        error: function () {
            alert('Error Post');
        }
    });
Comments