Использование Json.NET для привязки модели JSON
У меня есть метод, который публикуется через AJAX со следующим заголовком:
public JsonResult GetDocuments(string searchTerm, SortRequest sort)
Объект SortRequest определяется следующим образом:
[DataContract]
public class SortRequest
{
[DataMember(Name = "field")]
public string Field { get; set; }
[DataMember(Name = "dir")]
public string Direction { get; set; }
}
Из-за устаревшего кода объект JSON имеет имя свойства "dir", которое напрямую не совпадает с именем свойства C#. Мы хотим использовать Json.NET как связыватель модели для запросов JSON, потому что он может обрабатывать это, но проблема в том, что JSON, входящий в связыватель модели, выглядит как один объект с двумя свойствами верхнего уровня, "searchTerm" и "сортировка". Затем процесс десериализации пытается отобразить всю строку JSON в каждый параметр метода, что, очевидно, не удается.
Я попытался просмотреть теперь открытый исходный код .NET MVC и до сих пор не смог определить, как класс DefaultModelBinder обрабатывает это изящно. Единственный вариант, который я вижу до сих пор, - это преобразовать каждое действие JSON в один параметр запроса, но это не кажется хорошим решением, так как DefaultModelBinder этого не делает требовать этого.
Изменить для уточнения:
Строка запроса JSON выглядит примерно так:
{
"searchTerm": "test",
"sort": {
"field": "name",
"dir": "asc"
}
}
Мы переопределяем DefaultModelBinder и используем только Json.NET если запрос имеет тип application/json. Вот соответствующий код:
var request = controllerContext.HttpContext.Request;
request.InputStream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(request.InputStream))
{
var jsonString = reader.ReadToEnd();
result = JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType);
}
BindingContext.ModelType будет установлен в String и SortRequest для каждого параметра в методе, но так как выше указан один объект JSON, он не сопоставляется ни с одним из этих типов и, таким образом, внутри сам метод, все установлено на значения по умолчанию.
2 ответа:
Я думаю, что атрибут
JsonProperty
может быть использован для этого следующим образом:[DataContract] public class SortRequest { [DataMember(Name = "field")] [JsonProperty("field")] public string Field { get; set; } [DataMember(Name = "dir")] [JsonProperty("dir")] public string Direction { get; set; } }
Обновить
На основе json добавьте префикс привязки:
public JsonResult GetDocuments(string searchTerm, [Bind(Prefix="sort"] SortRequest sort)
В итоге я решил использовать решение с помощью JToken.Метод Parse в Json.NET библиотека. По существу, происходит то, что мы проверяем свойства верхнего уровня объекта JSON и видим, существует ли текущий параметр действия, к которому мы пытаемся привязаться. Это происходит, если имеется перекрытие между именем параметра действия и именем свойства одного передаваемого запроса. Я думаю, что это достаточно крайний случай, чтобы позволить скользить, поскольку это потребует только один объект будет передан в действие, которое ожидает несколько.
Вот модифицированный метод BindModel:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { object result; if (IsJSONRequest(controllerContext)) { var request = controllerContext.HttpContext.Request; request.InputStream.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(request.InputStream)) { var jsonString = reader.ReadToEnd(); // Only parse non-empty requests. if (!String.IsNullOrWhiteSpace(jsonString)) { // Parse the JSON into a generic key/value pair object. var obj = JToken.Parse(jsonString); // If the string parsed and there is a top level property of the same // name as the parameter name we are looking for, use that property // as the JSON object to de-serialize. if (obj != null && obj.HasValues && obj[bindingContext.ModelName] != null) { jsonString = obj[bindingContext.ModelName].ToString(); } } result = JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType); } } else { result = base.BindModel(controllerContext, bindingContext); } return result; }