18

I have a controller that is returning large JSON objects to jQuery Flot and I was wondering how easy it would be to replace the default JavaScriptSerializer with something faster like the one from ServiceStack.Text.

It would be good if I could change stuff like this using the DependencyResolver, but I suppose if absolutely everything was resolved this was, it could get pretty slow.

Tim
  • 7,746
  • 3
  • 49
  • 83
  • related: http://stackoverflow.com/questions/7109967/using-json-net-as-default-json-serializer-in-asp-net-mvc-3-is-it-possible – Ruben Bartelink Jul 20 '12 at 08:10
  • 1
    I asked this question 2 weeks before the linked duplicate... That makes the other question the duplicate! – Tim Nov 05 '14 at 14:31

2 Answers2

42

your best bet is to inherit from JsonResult class and override Execute method like

public class CustomJsonResult: JsonResult
{
    public CustomJsonResult()
    {
       JsonRequestBehavior = JsonRequestBehavior.DenyGet;
    }
    public override void ExecuteResult(ControllerContext context) {
            if (context == null) {
                throw new ArgumentNullException("context");
            }
            if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
                String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {
                throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
            }

            HttpResponseBase response = context.HttpContext.Response;

            if (!String.IsNullOrEmpty(ContentType)) {
                response.ContentType = ContentType;
            }
            else {
                response.ContentType = "application/json";
            }
            if (ContentEncoding != null) {
                response.ContentEncoding = ContentEncoding;
            }
            if (Data != null) {
                CustomJsSerializer serializer = new CustomJsSerializer();
                response.Write(serializer.Serialize(Data));
            }
        }
}

code is taken from JsonResult class of mvc3 and changed this line

JavaScriptSerializer serializer = new JavaScriptSerializer();

to

CustomJsSerializer serializer = new CustomJsSerializer();

you can use this class in action method like

public JsonResult result()
{
    var model = GetModel();
    return new CustomJsonResult{Data = model};
}

Additionally you can override json method of Controller class in your Base controller like

public class BaseController:Controller
{
   protected internal override JsonResult Json(object data)
        {
            return new CustomJsonResult { Data = data };
        }
}

now if you have all your controllers from BaseController then return Json(data) will call your serialization scheme. There are also other overloads of Json method that you may choose to override.

Muhammad Adeel Zahid
  • 17,474
  • 14
  • 90
  • 155
  • In asp.net MVC 3 the method - JsonResult Json(object data) is not virtual :( – Maxim Aug 03 '11 at 08:45
  • 6
    No, but JsonResult Json(object data, string contentType, Encoding contentEncoding) is, and the other one just delegates there. So happy overriding :) – asgerhallas Aug 22 '11 at 17:08
  • 4
    Apparently, you need to override the one with 4 parameters: protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) – Daniel Aug 23 '12 at 20:22
  • Hi, I have a ActionResult returning a PartialView(customModel), how can I make that the customModel uses the formatter? – VAAA Nov 26 '12 at 21:08
  • Please post a separate question and show the code you have and give us link to that question. – Muhammad Adeel Zahid Nov 27 '12 at 06:27
  • 7
    Is it just me or is MvcResources.JsonRequest_GetNotAllowed marked as internal? – Nicholi Dec 07 '12 at 02:38
  • Can this be done with ValueProviderFactory? (this way you won't have to change existing controllers code) – Uri Abramson Nov 27 '13 at 17:17
  • @MuhammadAdeelZahid: I don't think the constructor is needed in `CustomJsonResult`... looks like `JsonRequestBehavior` is already set to `JsonRequestBehavior.DenyGet` in the parent constructor: [JsonResult](https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Mvc/JsonResult.cs) – Hooman Bahreini Aug 23 '23 at 22:20
5

I'm adding this answer simply because I'm using an alternate solution that doesn't require overriding the System.Web.Mvc.Controller class. I add the following extension methods to the System.Web.Mvc.Controller class. The only "benefit" of this solution is that it doesn't require you to change the base class of the code generated Controller classes. Otherwise, it is functionally equivalent to the accepted answer.

public static JsonResult ToJsonResult(this Controller controller, 
                                          object target, 
                                          string contentType, 
                                          Encoding contentEncoding,
                                          JsonRequestBehavior behavior)
    {
        if (target != null)
        {
            if (target.GetType().HasAttribute<DataContractAttribute>())
            {
                return new DataContractJsonResult() { ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior, Data = target };
            }
        }
        return new JsonResult() { ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior, Data = target };
    }

    public static JsonResult ToJsonResult(this Controller controller, object target)
    {
        return controller.ToJsonResult(target, null, null, JsonRequestBehavior.DenyGet);
    }

    public static JsonResult ToJsonResult(this Controller controller, object target, string contentType)
    {
        return controller.ToJsonResult(target, contentType, null, JsonRequestBehavior.DenyGet);
    }

    public static JsonResult ToJsonResult(this Controller controller, object target, string contentType, Encoding contentEncoding)
    {
        return controller.ToJsonResult(target, contentType, contentEncoding, JsonRequestBehavior.DenyGet);
    }

    public static JsonResult ToJsonResult(this Controller controller, object target, string contentType, JsonRequestBehavior behavior)
    {
        return controller.ToJsonResult(target, contentType, null, behavior);
    }

In my application, I override the default controller and use the JSON.NET serializer if the type has the DataContract attribute. This functionality is encapsulated in the DataContractJsonResult class, which is not included, but is modeled after the class in the accepted answer to this question.

Joe Enzminger
  • 11,110
  • 3
  • 50
  • 75