CoffeeCode
CoffeeCode

Reputation: 4314

JSON not binding correctly in WebApi action

I'm creating and api using asp.net webapi
For some reasons this json:

JSON => {"page":"1","count":"10","sorting[name]":"asc"}
JSONString = >"{\"page\":\"1\",\"count\":\"10\",\"sorting[name]\":\"asc\"}"

Does not get bindend correctly to such a model:

 public class DataSourceRequestParams
    {
        public int Page { get; set; }
        public int Count { get; set; }
        public IDictionary<string, string> Sorting { get; set; }
        public IDictionary<string, string> Filter { get; set; }
        public IDictionary<string, string> Order { get; set; }
    }

The Sorting property does not get binded.

[HttpPost]
        public PagingResult<ApplicationUser> Get([FromBody] DataSourceRequestParams @params)
        {...}

If I create an action in an MVC application and pass the same JSON it gets binded.


Am I missing something here?

Upvotes: 0

Views: 319

Answers (1)

petelids
petelids

Reputation: 12815

The binding in Web API expects a Dictionary as a JSON object when the Content-Type is application/json so the following JSON will bind correctly with your model:

{"page":"1","count":"10","sorting":{"name":"asc"}}

Edit

Give that you can't change the JSON being passed in you will need to manually bind the JSON to your object. There are a couple of options here. You could write your own IModelBinder or you could override the behaviour of the JSON binder by writing your own JsonConverter.

As a rough example, you could create a JsonConverter like this:

public class DictionaryConverter : JsonConverter
{
    public DictionaryConverter()
    {
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //I've not implemented writing the Json
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //create a new object
        var temp = new DataSourceRequestParams();

        temp.Sorting = new Dictionary<string, string>();
        temp.Filter = new Dictionary<string, string>();
        temp.Order = new Dictionary<string, string>();

        //load the input into a JObject and grab the "simple" values
        JObject jsonObject = JObject.Load(reader);
        temp.Page = jsonObject["page"].Value<int>();
        temp.Count = jsonObject["count"].Value<int>();

        //parse the dictionary values
        AddValuesToDictionary(temp, jsonObject, "sorting", temp.Sorting);
        AddValuesToDictionary(temp, jsonObject, "filter", temp.Filter);
        AddValuesToDictionary(temp, jsonObject, "order", temp.Order);

        return temp;
    }

    private void AddValuesToDictionary(DataSourceRequestParams test, JObject jsonObject, string name, IDictionary<string, string> dictionary)
    {
        //grab each matching property
        var properties = jsonObject.Properties().Where(j => j.Name.StartsWith(name));
        if (properties != null)
        {
            foreach (var property in properties)
            {
                /*for each matched property grab the value between the brackets 
                 * from the name and the property value
                 * and the associate json value and add it to the dictionary
                 */
                dictionary.Add(Regex.Match(property.Name, @"\[([^\]]*)\]").Groups[1].Value, property.Value.Value<string>());                    
            }
        }
    }

    public override bool CanConvert(Type objectType)
    {
        //we can convert if the type is DataSourceRequestParams
        return typeof(DataSourceRequestParams).IsAssignableFrom(objectType);
    }
}

Then register the new converter in the GlobalConfiguration:

GlobalConfiguration.Configuration.Formatters.JsonFormatter
     .SerializerSettings.Converters.Add(new DictionaryConverter());

Then your original JSON will parse correctly. Obviously that code is devoid of all error handling etc so it's not production ready.

Upvotes: 1

Related Questions