Custom JsonResult Class for ASP.Net MVC to Avoid MaxJsonLength Exceeded Exception
January 3, 2011 30 Comments
Shortly before Christmas, I was working on an application that sent a large data set to jqGrid using an Ajax JSON stream in ASP.Net MVC. We were trying out using a “get everything once” model where all of the I/O happens when jqGrid is initialized or reset and then all of the data is available on the client for fast sort and filter. It was working well with test data of ~5-6K rows until a colleague checked in a change that added a new column. Suddenly my jqGrid was blank while hers (with a different, somewhat smaller, set of test data) was fine. Usually this sort of behavior from jqGrid indicates that the JSON was broken. Sure enough when I fired up my Fiddler HTTP debugger, I saw an error 500 for the JSON ajax query.
Server Error in ‘/’ Application.
Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. |
Stack Trace:
|
Version Information: Microsoft .NET Framework Version:2.0.50727.4952; ASP.NET Version:2.0.50727.4955
The error is inside of a call from MVC to BCL
It turns out that the exception happens inside of JsonResult.ExecuteResult(ControllerContext context). What is going on in there? Well, fortunately, ASP.Net MVC code is open source (MS-PL) and the .NET Framework class library source code is available for reference as well. Let’s take a look.
The meat of JsonResult.ExecuteResult(ControllerContext)
if (Data != null) { JavaScriptSerializer serializer = new JavaScriptSerializer(); response.Write(serializer.Serialize(Data)); }
The meat of JavaScriptSerializer.Serialize(object obj)
public string Serialize(object obj) { return Serialize(obj, SerializationFormat.JSON); } private string Serialize(object obj, SerializationFormat serializationFormat) { StringBuilder sb = new StringBuilder(); Serialize(obj, sb, serializationFormat); return sb.ToString(); } internal void Serialize(object obj, StringBuilder output, SerializationFormat serializationFormat) { SerializeValue(obj, output, 0, null, serializationFormat); // DevDiv Bugs 96574: Max JSON length does not apply when serializing to Javascript for ScriptDescriptors if (serializationFormat == SerializationFormat.JSON && output.Length > MaxJsonLength) { throw new InvalidOperationException(AtlasWeb.JSON_MaxJsonLengthExceeded); } }
JavaScriptSerializer completely serializes object obj into StringBuilder output and then, having allocated that memory, checks the size of the StringBuilder and if it is larger than the MaxJsonLength property it throws an InvalidOperationException. JsonResult just creates a new JavaScriptSerializer and uses it so there is no way to change the default MaxJsonLength when using JsonResult in MVC. Since the memory is allocated before the InvalidOperationException is thrown, I’m not really clear what the point of MaxJsonLength is this deep in the framework. Surely whatever is going to use the JSON string would be in a better position to decide if the string returned by JavaScriptSerializer.Serialize() was too long to use?
Anyway, we have the problem isolated now for a solution. We need to implement our own ActionResult that will generate JSON while allowing the caller to twiddle the knobs on JavaScriptSerializer.
LargeJsonResult ActionResult class
using System; using System.Web.Script.Serialization; namespace System.Web.Mvc { public class LargeJsonResult : JsonResult { const string JsonRequest_GetNotAllowed = "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet."; public LargeJsonResult() { MaxJsonLength = 1024000; RecursionLimit = 100; } public int MaxJsonLength { get; set; } public int RecursionLimit { get; set; } 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( 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 ) { JavaScriptSerializer serializer = new JavaScriptSerializer() { MaxJsonLength = MaxJsonLength, RecursionLimit = RecursionLimit }; response.Write( serializer.Serialize( Data ) ); } } } }
You can use return new LargeJsonResult(){ Data = data } from any Action method where you would have used return Json(data). Also, you have direct control over the MaxJsonLength and RecursionLimit properites of JavaScriptSerializer.
return new LargeJsonResult() { Data = output, MaxJsonLength = int.MaxValue };
Pingback: MVC3 Unable To Return Large JSON Even with Web Config Patch, Using LargeJsonResult Instead | PeterKellner.net
you can set a maxJsonLength in config file 😉
@aruss: Sorry. You can’t set the maxJsonLenght for ASP.Net MVC in a config because the JsonResult class implemented by ASP.NET MVC doesn’t use any configuration data when it creates a new JavaScriptSerializer. Did you actually read the article?
yes, but for solving this problem you dont need extra properties for your custom result, just read the values from config files and set the propper properties on a JavaScriptSerializer and then register it in a custom result, you can even config custom converters, that was what i mentioned.
ScriptingJsonSerializationSection section = ConfigurationManager.GetSection(“system.web.extensions/scripting/webServices/jsonSerialization”) as ScriptingJsonSerializationSection;
😉
Thanks so much for posting this, Brian! Really helpful.
Thanks for this, it really helped.
@aruss: how exactly would you use it?
Thanks. It helped
Awesome post… worked perfectly
Pingback: Large JSON Result for Telerik’s MVC Grid « Raymund Macaalay's Dev Blog
This is great! One suggestion. According to this doc
http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.maxjsonlength.aspx
the default is for maxJsonLength is 2097152.
“Type: System.Int32
The maximum length of JSON strings. The default is 2097152 characters, which is equivalent to 4 MB of Unicode string data.”
So I will use a higher default.
Rob
Good work, brother. Just what the doctor ordered.
Goood job, real thanks man!
Thanks Brian.
Thanks a lot. Was very useful
You’re a bloody life saver.
Kudos
BTW. You can inject the LargeJsonResult class across the board by overriding one of the Json() methods from Controller:
/* replace the standard JsonResult object produced by all Json() method overloads with LargJsonResult which does not have an arbitrary size limit. */
protected override JsonResult Json( object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior )
{
return new LargeJsonResult() { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior };
}
THANK YOU!!
Got into the same problem using jQuery FullCalendar. Your article saved my day.
Simply beautiful.
Pingback: Telerik MVC Grid, JsonResult and Dependency Injection | Finding the Solution
Pingback: Increase json response maxJsonLength in MVC 4 | BlogoSfera
Pingback: Increse json response maxJsonLength in MVC 4
Don’t see the point of the custom class. This works just as well
return new JsonResult { Data = Result, MaxJsonLength = Int32.MaxValue };
where “Result” is that data you wish to serialize.
It solved my problem. Thanks friend.
Pingback: MaxJsonLength exception in ASP.NET MVC during JavaScriptSerializer | Ask Programming & Technology
Wow,this is really great solution.Hats off.
Pingback: [RESOLVED]JsonResult and MaxJsonLength? | ASP Questions & Answers
Excellent, worked like a charm for me, appreciate the effort you took in posting this!
This worked perfectly, thank you
Pingback: How to resolve JSON serialization error in Telerik MVC grid to populate large dataset | jenyamatya