Usando JSON.NET como el serializador JSON predeterminado en ASP.NET MVC 3, ¿es posible?

¿Es posible utilizar JSON.NET como serializador JSON predeterminado en ASP.NET MVC 3?

De acuerdo con mi investigación, parece que la única forma de lograr esto es extender ActionResult ya que JsonResult en MVC3 no es virtual …

Esperé que con ASP.NET MVC 3 hubiera una forma de especificar un proveedor enchufable para serializar a JSON.

¿Pensamientos?

Creo que la mejor manera de hacerlo es, como se describe en los enlaces, extender ActionResult o extender JsonResult directamente.

En cuanto al método JsonResult que no es virtual en el controlador que no es verdadero, simplemente elija la sobrecarga correcta. Esto funciona bien:

protected override JsonResult Json(object data, string contentType, Encoding contentEncoding) 

EDIT 1 : una extensión JsonResult …

 public class JsonNetResult : JsonResult { public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); var response = context.HttpContext.Response; response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json"; if (ContentEncoding != null) response.ContentEncoding = ContentEncoding; // If you need special handling, you can call another form of SerializeObject below var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented); response.Write(serializedObject); } 

EDIT 2 : eliminé el cheque de datos que son nulos según las sugerencias a continuación. Eso debería hacer que las versiones más recientes de JQuery sean felices y parece lo más sensato que se puede hacer, ya que la respuesta puede deserializarse de manera incondicional. Tenga en cuenta, sin embargo, que este no es el comportamiento predeterminado para las respuestas JSON de ASP.NET MVC, que en cambio responde con una cadena vacía, cuando no hay datos.

Implementé esto sin la necesidad de un controlador base o inyección.

Usé filtros de acción para reemplazar el JsonResult con un JsonNetResult.

 public class JsonHandlerAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { var jsonResult = filterContext.Result as JsonResult; if (jsonResult != null) { filterContext.Result = new JsonNetResult { ContentEncoding = jsonResult.ContentEncoding, ContentType = jsonResult.ContentType, Data = jsonResult.Data, JsonRequestBehavior = jsonResult.JsonRequestBehavior }; } base.OnActionExecuted(filterContext); } } 

En Global.asax.cs Application_Start (), necesitaría agregar:

 GlobalFilters.Filters.Add(new JsonHandlerAttribute()); 

Para completar, esta es mi clase de extensión JsonNetResult que recogí de otro lado y que modifiqué ligeramente para obtener el soporte correcto de Steaming:

 public class JsonNetResult : JsonResult { public JsonNetResult() { Settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Error }; } public JsonSerializerSettings Settings { get; private set; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("JSON GET is not allowed"); HttpResponseBase response = context.HttpContext.Response; response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; if (this.ContentEncoding != null) response.ContentEncoding = this.ContentEncoding; if (this.Data == null) return; var scriptSerializer = JsonSerializer.Create(this.Settings); scriptSerializer.Serialize(response.Output, this.Data); } } 

Use el convertidor JSON de Newtonsoft:

 public ActionResult DoSomething() { dynamic cResponse = new ExpandoObject(); cResponse.Property1 = "value1"; cResponse.Property2 = "value2"; return Content(JsonConvert.SerializeObject(cResponse)); } 

Sé que esto está bien después de que la pregunta ha sido respondida, pero estoy usando un enfoque diferente ya que estoy usando la dependency injection para crear instancias de mis controladores.

He reemplazado el IActionInvoker (al inyectar la propiedad ControllerActionInvoker del controlador) con una versión que anula el método InvokeActionMethod.

Esto significa que no hay cambio en la herencia del controlador y se puede eliminar fácilmente cuando actualizo a MVC4 alterando el registro del contenedor DI para TODOS los controladores

 public class JsonNetActionInvoker : ControllerActionInvoker { protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary parameters) { ActionResult invokeActionMethod = base.InvokeActionMethod(controllerContext, actionDescriptor, parameters); if ( invokeActionMethod.GetType() == typeof(JsonResult) ) { return new JsonNetResult(invokeActionMethod as JsonResult); } return invokeActionMethod; } private class JsonNetResult : JsonResult { public JsonNetResult() { this.ContentType = "application/json"; } public JsonNetResult( JsonResult existing ) { this.ContentEncoding = existing.ContentEncoding; this.ContentType = !string.IsNullOrWhiteSpace(existing.ContentType) ? existing.ContentType : "application/json"; this.Data = existing.Data; this.JsonRequestBehavior = existing.JsonRequestBehavior; } public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet) && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { base.ExecuteResult(context); // Delegate back to allow the default exception to be thrown } HttpResponseBase response = context.HttpContext.Response; response.ContentType = this.ContentType; if (this.ContentEncoding != null) { response.ContentEncoding = this.ContentEncoding; } if (this.Data != null) { // Replace with your favourite serializer. new Newtonsoft.Json.JsonSerializer().Serialize( response.Output, this.Data ); } } } } 

— EDITAR – Actualizado para mostrar el registro del contenedor para los controladores. Estoy usando Unity aquí.

 private void RegisterAllControllers(List exportedTypes) { this.rootContainer.RegisterType(); Func isIController = typeof(IController).IsAssignableFrom; Func isIHttpController = typeof(IHttpController).IsAssignableFrom; foreach (Type controllerType in exportedTypes.Where(isIController)) { this.rootContainer.RegisterType( typeof(IController), controllerType, controllerType.Name.Replace("Controller", string.Empty), new InjectionProperty("ActionInvoker") ); } foreach (Type controllerType in exportedTypes.Where(isIHttpController)) { this.rootContainer.RegisterType(typeof(IHttpController), controllerType, controllerType.Name); } } public class UnityControllerFactory : System.Web.Mvc.IControllerFactory, System.Web.Http.Dispatcher.IHttpControllerActivator { readonly IUnityContainer container; public UnityControllerFactory(IUnityContainer container) { this.container = container; } IController System.Web.Mvc.IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) { return this.container.Resolve(controllerName); } SessionStateBehavior System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { return SessionStateBehavior.Required; } void System.Web.Mvc.IControllerFactory.ReleaseController(IController controller) { } IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return this.container.Resolve(controllerType.Name); } } 

Ampliando la respuesta de https://stackoverflow.com/users/183056/sami-beyoglu , si configura el tipo de contenido, jQuery podrá convertir los datos devueltos en un objeto para usted.

 public ActionResult DoSomething() { dynamic cResponse = new ExpandoObject(); cResponse.Property1 = "value1"; cResponse.Property2 = "value2"; return Content(JsonConvert.SerializeObject(cResponse), "application/json"); } 

Hice una versión que hace que las acciones del servicio web sean seguras y simples. Lo usas así:

 public JsonResult MyAction() { return new MyDataContract(); } 

La clase:

 public class JsonResult : JsonResult { public JsonResult(T data) { Data = data; JsonRequestBehavior = JsonRequestBehavior.AllowGet; } public override void ExecuteResult(ControllerContext context) { // Use Json.Net rather than the default JavaScriptSerializer because it's faster and better if (context == null) throw new ArgumentNullException("context"); var response = context.HttpContext.Response; response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json"; if (ContentEncoding != null) response.ContentEncoding = ContentEncoding; var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented); response.Write(serializedObject); } public static implicit operator JsonResult(T d) { return new JsonResult(d); } } 

Mi publicación puede ayudar a alguien.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; namespace MultipleSubmit.Service { public abstract class BaseController : Controller { protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) { return new JsonNetResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior }; } } } using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; namespace MultipleSubmit.Service { public class JsonNetResult : JsonResult { public JsonNetResult() { Settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Error }; } public JsonSerializerSettings Settings { get; private set; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals (context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("JSON GET is not allowed"); HttpResponseBase response = context.HttpContext.Response; response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; if (this.ContentEncoding != null) response.ContentEncoding = this.ContentEncoding; if (this.Data == null) return; var scriptSerializer = JsonSerializer.Create(this.Settings); using (var sw = new StringWriter()) { scriptSerializer.Serialize(sw, this.Data); response.Write(sw.ToString()); } } } } public class MultipleSubmitController : BaseController { public JsonResult Index() { var data = obj1; // obj1 contains the Json data return Json(data, JsonRequestBehavior.AllowGet); } }