Reputation: 4314
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
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