简体   繁体   中英

ASP.NET Core Web API Automatic JSON Parameter Deserialization Not Working

I've a simple controller that takes one parameter from the POST request body. Normally it should automatically deserialize it from JSON to the object type but it fails. When I try to deserialize it myself it works withot problems. Here is some code:

The controller (the documentCommand variable is null):

public async Task<IActionResult> Create([FromBody]CreateDocumentCommand documentCommand)
{
    if (documentCommand == null)
    {
        return StatusCode(403); //Deserialization fails, documentCommand is null
    }
    //we have to reach this :(
    return Json(documentCommand.Document.Id);
}

Here is how I serialize it and how I test if it will be able to deserialize it:

JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
{ 
    TypeNameHandling = TypeNameHandling.Auto,
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore
};
string serialized = JsonConvert.SerializeObject(item, jsonSerializerSettings);
CreateDocumentCommand deserialized = JsonConvert.DeserializeObject<CreateDocumentCommand>(serialized, jsonSerializerSettings);

In my CreateDocumentCommand class I have an interface property and when I remove the TypeNameHandling = TypeNameHandling.Auto it fails in the second example too.

Is there a way yo tell the MVC deserializer to take the TypeNameHandling into account? It seems to me that it skips it.

EDIT Some more code:

public class CreateDocumentCommand : Command, ICreateDocumentCommand
{
    public CreateDocumentCommand()
    {

    }

    public IDocument Document { get; set; }
}

MY SOLUTION: Added that ConcreteTypeConverter which I found at the link provided by Babak Naffas and made some changes because I was getting some circular reference exeptions. Also Added the [JsonConverter(typeof(ConcreteTypeConverter))] beefore the CreateDocumentCommand class.

public class ConcreteTypeConverter<T> : JsonConverter
{
    static bool read = false;

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        serializer.TypeNameHandling = TypeNameHandling.Auto;
        serializer.NullValueHandling = NullValueHandling.Ignore;
        serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
        return serializer.Deserialize<T>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }

    public override bool CanRead
    {
        get
        {
            read = !read;
            return read;
        }
    }
}

Your issue is the IDocument interface, which can't be deserialized out of the box as the deserializer can't know which concrete class to use.

Also, make sure to create a unit test that takes the raw JSON that you'd be passing to your controller method and make sure it can be deserialized.

If you want global setting then you can specify JsonSerializerSetting at application level using AddJsonOptions extension. Read it about here .

services.AddMvc()
.AddJsonOptions(opt =>
{
    if (opt.SerializerSettings != null)
    {
            opt.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto,
            opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore,
            opt.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore
    }
});

AddJsonOptions is defined in Microsoft.AspNetCore.Mvc.Formatters.Json nuget package.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM