#c# #asp.net-mvc-3 #datacontractserializer #modelbinders #model-binding
#c# #asp.net-mvc-3 #datacontractserializer #modelbinders #привязка модели
Вопрос:
MVC3 поставляется из коробки с JsonValueProviderFactory(), что очень удобно для привязки входящего JSON к модели. К сожалению, я не могу понять, как настроить контракты модели с именами, которые отличаются от входящего JSON. Например:
[DataContract(Name = "session")]
public class FacebookSession
{
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "expires")]
public int? Expires { get; set; }
[DataMember(Name = "secret")]
public string Secret { get; set; }
[DataMember(Name = "session_key")]
public string Sessionkey { get; set; }
[DataMember(Name = "sig")]
public string Signature { get; set; }
[DataMember(Name = "uid")]
public string UserId { get; set; }
}
при передаче объекта json, представляющего сеанс facebook, свойства secret и expires привязываются должным образом, но остальные этого не делают, потому что имя свойства отличается от имени ключа json. Я ожидал бы, что сериализатор datacontract попытается привязаться к имени, указанному в атрибуте, но, похоже, это не так. Есть ли у кого-нибудь предложения по обходному пути?
Редактировать
Пример того, как я бы использовал эту модель:
public ActionResult Log(int? custId, FacebookSession response)
{
ViewBag.Id = response.UserId;
return View();
}
Ответ №1:
В итоге я использовал пример привязки модели к ссылке gt124 вместе с лучшей привязкой модели для написания моей собственной логики привязки модели. В итоге это выглядело так:
public interface IFilteredModelBinder : IModelBinder
{
bool IsMatch(Type modelType);
}
public class SmartModelBinder : DefaultModelBinder
{
private readonly IFilteredModelBinder[] _filteredModelBinders;
public SmartModelBinder(IFilteredModelBinder[] filteredModelBinders)
{
_filteredModelBinders = filteredModelBinders;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
foreach (var filteredModelBinder in _filteredModelBinders)
{
if (filteredModelBinder.IsMatch(bindingContext.ModelType))
{
return filteredModelBinder.BindModel(controllerContext, bindingContext);
}
}
return base.BindModel(controllerContext, bindingContext);
}
}
public class NewtonsoftJsonModelBinder : IFilteredModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
var request = controllerContext.HttpContext.Request;
request.InputStream.Position = 0;
var incomingData = new StreamReader(request.InputStream).ReadToEnd();
if (String.IsNullOrEmpty(incomingData))
{
// no JSON data
return null;
}
object ret = JsonConvert.DeserializeObject(incomingData, bindingContext.ModelType);
return ret;
}
public bool IsMatch(Type modelType)
{
var ret = (typeof(JsonModel).IsAssignableFrom(modelType));
return ret;
}
}
Затем я использовал JSON.net атрибуты для сопоставления с различными свойствами объекта (вместо DataContracts) в моделях. Все модели унаследованы от пустого базового класса JSONModel.
Комментарии:
1. Убедитесь, что вы заменили связующее по умолчанию: в Application_Start:
ModelBinders.Binders.DefaultBinder = new SmartModelBinder(new [] {new NewtonsoftJsonModelBinder()});
Ответ №2:
Вы можете передать его в виде строки и вручную вызвать datacontractdeserializer, если только вы не напишете свой собственный modelbinder. Я полагаю, что связующее по умолчанию использует javascriptserializer, а не datacontractjsserializer.
Комментарии:
1. это не работает, ответ равен = нулю. Я имею в виду, я, вероятно, мог бы использовать JSON. Stringify() в вызове js, но это огорчило бы меня.
2. Приведенный выше пример может дать вам достаточно для написания modelbinder, который использует datacontractjsonserializer…
Ответ №3:
Вам не нужно заменять связующее по умолчанию, просто напишите подобный атрибут
public class DataContractJsonModelBinderAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new DataContractJsonModelBinder();
}
}
использование простое
[DataContract(Name = "session")]
[DataContractJsonModelBinder]
public class FacebookSession
{
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "expires")]
public int? Expires { get; set; }
[DataMember(Name = "secret")]
public string Secret { get; set; }
[DataMember(Name = "session_key")]
public string Sessionkey { get; set; }
[DataMember(Name = "sig")]
public string Signature { get; set; }
[DataMember(Name = "uid")]
public string UserId { get; set; }
}
ОБНОВЛЕНИЕ Теперь y может просто использовать встроенный Json.Подобная функциональность СЕТИ:
[JsonObject]
public class FacebookSession
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
}
и при необходимости
var facebokSession = JsonConvert.DeserializeObject<FacebookSession>(facebookSessionJsonString);