Controller.Json function slow for IQueryable - json

I have an mvc controller action which return json and it take around 2 secs and I expect this to be faster, under 1 secs. I was profilling the contoller action and found out that line with the return json is slow, until there It is executed less that 100ms.
Is it because of I am reaching that line with the actual sql to linq query with IQueryable interface and only there DB query is executed and it does something like "toList()". I would like to know what is actually happening there?
or is the Controller.Json function is in general slow and I can use something better?
public ActionResult GetItems()
{
IQueryable<Item> Items = default(IQueryable<Item>);
Items = myComponent.getItems(); //returns IQueryable
var result = Items.OrderByDescending(m => m.category).ThenBy(m => m.order);
return Json(result, JsonRequestBehavior.AllowGet);
}

There is possibility of two place where you can tune:
IQuerable it self
Controller.Json's Serialization method
For the IQuerable query, you could try sql profiler or try call ToList() before return it to see how long it take and possibly optimise it
(without more knowledge of your query, I can not help you).
But for the second part, you can try serialize the result using library like Json.Net
As from the source code, you can see that Mvc use JavaScriptSerializer to do the json serialization, when you call the Json(). (you can also see what actually happen by looking at the source code)
From json.net website's comparison chart, the performace for JavaScriptSerializer is quite bad.
So you could try implement your own JsonActionResult using Json.Net, some sample code below:
Source from : Using JSON.NET to return ActionResult
In your Controller (or base Controller)
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonNetResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}
And the definition for JsonNetResult:
public class JsonNetResult : JsonResult
{
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
}
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());
}
}
}

Related

How to send very long json to Asp.NET MVC [duplicate]

I am using the autocomplete feature of jQuery. When I try to retrieve the list of more then 17000 records (each won't have more than 10 char length), it's exceeding the length and throws the error:
Exception information:
Exception type: InvalidOperationException
Exception message: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
Can I set an unlimited length for maxJsonLength in web.config? If not, what is the maximum length I can set?
NOTE: this answer applies only to Web services, if you are returning JSON from a Controller method, make sure you read this SO answer below as well: https://stackoverflow.com/a/7207539/1246870
The MaxJsonLength property cannot be unlimited, is an integer property that defaults to 102400 (100k).
You can set the MaxJsonLength property on your web.config:
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="50000000"/>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
If you are using MVC 4, be sure to check out this answer as well.
If you are still receiving the error:
after setting the maxJsonLength property to its maximum value in web.config
and you know that your data's length is less than this value
and you are not utilizing a web service method for the JavaScript serialization
your problem is is likely that:
The value of the MaxJsonLength property applies only to the internal JavaScriptSerializer instance that is used by the asynchronous communication layer to invoke Web services methods. (MSDN: ScriptingJsonSerializationSection.MaxJsonLength Property)
Basically, the "internal" JavaScriptSerializer respects the value of maxJsonLength when called from a web method; direct use of a JavaScriptSerializer (or use via an MVC action-method/Controller) does not respect the maxJsonLength property, at least not from the systemWebExtensions.scripting.webServices.jsonSerialization section of web.config. In particular, the Controller.Json() method does not respect the configuration setting!
As a workaround, you can do the following within your Controller (or anywhere really):
var serializer = new JavaScriptSerializer();
// For simplicity just use Int32's max value.
// You could always read the value from the config section mentioned above.
serializer.MaxJsonLength = Int32.MaxValue;
var resultData = new { Value = "foo", Text = "var" };
var result = new ContentResult{
Content = serializer.Serialize(resultData),
ContentType = "application/json"
};
return result;
This answer is my interpretation of this asp.net forum answer.
In MVC 4 you can do:
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonResult()
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior,
MaxJsonLength = Int32.MaxValue
};
}
in your controller.
Addition:
For anyone puzzled by the parameters you need to specify, a call could look like this:
Json(
new {
field1 = true,
field2 = "value"
},
"application/json",
Encoding.UTF8,
JsonRequestBehavior.AllowGet
);
You can configure the max length for json requests in your web.config file:
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="....">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
The default value for maxJsonLength is 102400. For more details, see this MSDN page: http://msdn.microsoft.com/en-us/library/bb763183.aspx
if you are still getting error after web.config setting like following:
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="50000000"/>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
I solved it by following:
public ActionResult/JsonResult getData()
{
var jsonResult = Json(superlargedata, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
I hope this should help.
I was having this problem in ASP.NET Web Forms. It was completely ignoring the web.config file settings so I did this:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
return serializer.Serialize(response);
Of course overall this is terrible practice. If you are sending this much data in a web service call you should look at a different approach.
I followed vestigal's answer and got to this solution:
When I needed to post a large json to an action in a controller, I would get the famous "Error during deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.\r\nParameter name: input value provider".
What I did is create a new ValueProviderFactory, LargeJsonValueProviderFactory, and set the MaxJsonLength = Int32.MaxValue in the GetDeserializedObject method
public sealed class LargeJsonValueProviderFactory : ValueProviderFactory
{
private static void AddToBackingStore(LargeJsonValueProviderFactory.EntryLimitedDictionary backingStore, string prefix, object value)
{
IDictionary<string, object> dictionary = value as IDictionary<string, object>;
if (dictionary != null)
{
foreach (KeyValuePair<string, object> keyValuePair in (IEnumerable<KeyValuePair<string, object>>) dictionary)
LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);
}
else
{
IList list = value as IList;
if (list != null)
{
for (int index = 0; index < list.Count; ++index)
LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakeArrayKey(prefix, index), list[index]);
}
else
backingStore.Add(prefix, value);
}
}
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return (object) null;
string end = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
if (string.IsNullOrEmpty(end))
return (object) null;
var serializer = new JavaScriptSerializer {MaxJsonLength = Int32.MaxValue};
return serializer.DeserializeObject(end);
}
/// <summary>Returns a JSON value-provider object for the specified controller context.</summary>
/// <returns>A JSON value-provider object for the specified controller context.</returns>
/// <param name="controllerContext">The controller context.</param>
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
object deserializedObject = LargeJsonValueProviderFactory.GetDeserializedObject(controllerContext);
if (deserializedObject == null)
return (IValueProvider) null;
Dictionary<string, object> dictionary = new Dictionary<string, object>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
LargeJsonValueProviderFactory.AddToBackingStore(new LargeJsonValueProviderFactory.EntryLimitedDictionary((IDictionary<string, object>) dictionary), string.Empty, deserializedObject);
return (IValueProvider) new DictionaryValueProvider<object>((IDictionary<string, object>) dictionary, CultureInfo.CurrentCulture);
}
private static string MakeArrayKey(string prefix, int index)
{
return prefix + "[" + index.ToString((IFormatProvider) CultureInfo.InvariantCulture) + "]";
}
private static string MakePropertyKey(string prefix, string propertyName)
{
if (!string.IsNullOrEmpty(prefix))
return prefix + "." + propertyName;
return propertyName;
}
private class EntryLimitedDictionary
{
private static int _maximumDepth = LargeJsonValueProviderFactory.EntryLimitedDictionary.GetMaximumDepth();
private readonly IDictionary<string, object> _innerDictionary;
private int _itemCount;
public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
{
this._innerDictionary = innerDictionary;
}
public void Add(string key, object value)
{
if (++this._itemCount > LargeJsonValueProviderFactory.EntryLimitedDictionary._maximumDepth)
throw new InvalidOperationException("JsonValueProviderFactory_RequestTooLarge");
this._innerDictionary.Add(key, value);
}
private static int GetMaximumDepth()
{
NameValueCollection appSettings = ConfigurationManager.AppSettings;
if (appSettings != null)
{
string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
int result;
if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
return result;
}
return 1000;
}
}
}
Then, in the Application_Start method from Global.asax.cs, replace the ValueProviderFactory with the new one:
protected void Application_Start()
{
...
//Add LargeJsonValueProviderFactory
ValueProviderFactory jsonFactory = null;
foreach (var factory in ValueProviderFactories.Factories)
{
if (factory.GetType().FullName == "System.Web.Mvc.JsonValueProviderFactory")
{
jsonFactory = factory;
break;
}
}
if (jsonFactory != null)
{
ValueProviderFactories.Factories.Remove(jsonFactory);
}
var largeJsonValueProviderFactory = new LargeJsonValueProviderFactory();
ValueProviderFactories.Factories.Add(largeJsonValueProviderFactory);
}
I fixed it.
//your Json data here
string json_object="........";
JavaScriptSerializer jsJson = new JavaScriptSerializer();
jsJson.MaxJsonLength = 2147483644;
MyClass obj = jsJson.Deserialize<MyClass>(json_object);
It works very well.
if, after implementing the above addition into your web.config, you get an “Unrecognized configuration section system.web.extensions.” error then try adding this to your web.config in the <ConfigSections> section:
<sectionGroup name="system.web.extensions" type="System.Web.Extensions">
<sectionGroup name="scripting" type="System.Web.Extensions">
<sectionGroup name="webServices" type="System.Web.Extensions">
<section name="jsonSerialization" type="System.Web.Extensions"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
Simply set MaxJsonLength proprty in MVC's Action method
JsonResult json= Json(classObject, JsonRequestBehavior.AllowGet);
json.MaxJsonLength = int.MaxValue;
return json;
you can write this line into Controller
json.MaxJsonLength = 2147483644;
you can also write this line into web.config
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
`
To be on the safe side, use both.
Fix for ASP.NET MVC if you want to fix it only for particular action that is causing the problem then change this code:
public JsonResult GetBigJson()
{
var someBigObject = GetBigObject();
return Json(someBigObject);
}
to this:
public JsonResult GetBigJson()
{
var someBigObject = GetBigObject();
return new JsonResult()
{
Data = someBigObject,
JsonRequestBehavior = JsonRequestBehavior.DenyGet,
MaxJsonLength = int.MaxValue
};
}
And the functionality should be same, you can just return bigger JSON as response.
Explanation based on ASP.NET MVC source code: you can check what Controller.Json method does in ASP.NET MVC source code
protected internal JsonResult Json(object data)
{
return Json(data, null /* contentType */, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
}
It is calling other Controller.Json method:
protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}
where passed contentType and contentEncoding object are null. So basically calling return Json(object) in controller is equivalent to calling return new JsonResult { Data = object, JsonRequestBehavior = sonRequestBehavior.DenyGet }. You can use second form and parameterize JsonResult.
So what happens when you set MaxJsonLength property (by default it's null)?
It's passed down to JavaScriptSerializer.MaxJsonLength property and then JavaScriptSerializer.Serialize method is called :
JavaScriptSerializer serializer = new JavaScriptSerializer();
if (MaxJsonLength.HasValue)
{
serializer.MaxJsonLength = MaxJsonLength.Value;
}
if (RecursionLimit.HasValue)
{
serializer.RecursionLimit = RecursionLimit.Value;
}
response.Write(serializer.Serialize(Data));
And when you don't set MaxJsonLenght property of serializer then it takes default value which is just 2MB.
If you are getting this error from the MiniProfiler in MVC then you can increase the value by setting the property MiniProfiler.Settings.MaxJsonResponseSize to the desired value. By default, this tool seems to ignore the value set in config.
MiniProfiler.Settings.MaxJsonResponseSize = 104857600;
Courtesy mvc-mini-profiler.
I suggest setting it to Int32.MaxValue.
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
How about some attribute magic?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class MaxJsonSizeAttribute : ActionFilterAttribute
{
// Default: 10 MB worth of one byte chars
private int maxLength = 10 * 1024 * 1024;
public int MaxLength
{
set
{
if (value < 0) throw new ArgumentOutOfRangeException("value", "Value must be at least 0.");
maxLength = value;
}
get { return maxLength; }
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
JsonResult json = filterContext.Result as JsonResult;
if (json != null)
{
if (maxLength == 0)
{
json.MaxJsonLength = int.MaxValue;
}
else
{
json.MaxJsonLength = maxLength;
}
}
}
}
Then you could either apply it globally using the global filter configuration or controller/action-wise.
If you are encountering this sort of issue in View, you can use below method to resolve that. Here Iused Newtonsoft package .
#using Newtonsoft.Json
<script type="text/javascript">
var partData = #Html.Raw(JsonConvert.SerializeObject(ViewBag.Part));
</script>
Alternative ASP.NET MVC 5 Fix:
(Mine is similar to MFCs answer above with a few small changes)
I wasn't ready to change to Json.NET just yet and in my case the error was occurring during the request. Best approach in my scenario was modifying the actual JsonValueProviderFactory which applies the fix to the global project and can be done by editing the global.cs file as such.
JsonValueProviderConfig.Config(ValueProviderFactories.Factories);
add a web.config entry:
<add key="aspnet:MaxJsonLength" value="20971520" />
and then create the two following classes
public class JsonValueProviderConfig
{
public static void Config(ValueProviderFactoryCollection factories)
{
var jsonProviderFactory = factories.OfType<JsonValueProviderFactory>().Single();
factories.Remove(jsonProviderFactory);
factories.Add(new CustomJsonValueProviderFactory());
}
}
This is basically an exact copy of the default implementation found in System.Web.Mvc but with the addition of a configurable web.config appsetting value aspnet:MaxJsonLength.
public class CustomJsonValueProviderFactory : ValueProviderFactory
{
/// <summary>Returns a JSON value-provider object for the specified controller context.</summary>
/// <returns>A JSON value-provider object for the specified controller context.</returns>
/// <param name="controllerContext">The controller context.</param>
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
object deserializedObject = CustomJsonValueProviderFactory.GetDeserializedObject(controllerContext);
if (deserializedObject == null)
return null;
Dictionary<string, object> strs = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
CustomJsonValueProviderFactory.AddToBackingStore(new CustomJsonValueProviderFactory.EntryLimitedDictionary(strs), string.Empty, deserializedObject);
return new DictionaryValueProvider<object>(strs, CultureInfo.CurrentCulture);
}
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return null;
string fullStreamString = (new StreamReader(controllerContext.HttpContext.Request.InputStream)).ReadToEnd();
if (string.IsNullOrEmpty(fullStreamString))
return null;
var serializer = new JavaScriptSerializer()
{
MaxJsonLength = CustomJsonValueProviderFactory.GetMaxJsonLength()
};
return serializer.DeserializeObject(fullStreamString);
}
private static void AddToBackingStore(EntryLimitedDictionary backingStore, string prefix, object value)
{
IDictionary<string, object> strs = value as IDictionary<string, object>;
if (strs != null)
{
foreach (KeyValuePair<string, object> keyValuePair in strs)
CustomJsonValueProviderFactory.AddToBackingStore(backingStore, CustomJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);
return;
}
IList lists = value as IList;
if (lists == null)
{
backingStore.Add(prefix, value);
return;
}
for (int i = 0; i < lists.Count; i++)
{
CustomJsonValueProviderFactory.AddToBackingStore(backingStore, CustomJsonValueProviderFactory.MakeArrayKey(prefix, i), lists[i]);
}
}
private class EntryLimitedDictionary
{
private static int _maximumDepth;
private readonly IDictionary<string, object> _innerDictionary;
private int _itemCount;
static EntryLimitedDictionary()
{
_maximumDepth = CustomJsonValueProviderFactory.GetMaximumDepth();
}
public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
{
this._innerDictionary = innerDictionary;
}
public void Add(string key, object value)
{
int num = this._itemCount + 1;
this._itemCount = num;
if (num > _maximumDepth)
{
throw new InvalidOperationException("The length of the string exceeds the value set on the maxJsonLength property.");
}
this._innerDictionary.Add(key, value);
}
}
private static string MakeArrayKey(string prefix, int index)
{
return string.Concat(prefix, "[", index.ToString(CultureInfo.InvariantCulture), "]");
}
private static string MakePropertyKey(string prefix, string propertyName)
{
if (string.IsNullOrEmpty(prefix))
{
return propertyName;
}
return string.Concat(prefix, ".", propertyName);
}
private static int GetMaximumDepth()
{
int num;
NameValueCollection appSettings = ConfigurationManager.AppSettings;
if (appSettings != null)
{
string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
if (values != null && values.Length != 0 && int.TryParse(values[0], out num))
{
return num;
}
}
return 1000;
}
private static int GetMaxJsonLength()
{
int num;
NameValueCollection appSettings = ConfigurationManager.AppSettings;
if (appSettings != null)
{
string[] values = appSettings.GetValues("aspnet:MaxJsonLength");
if (values != null && values.Length != 0 && int.TryParse(values[0], out num))
{
return num;
}
}
return 1000;
}
}
For those who are having issues with in MVC3 with JSON that's automatically being deserialized for a model binder and is too large, here is a solution.
Copy the code for the JsonValueProviderFactory class from the MVC3 source code into a new class.
Add a line to change the maximum JSON length before the object is deserialized.
Replace the JsonValueProviderFactory class with your new, modified class.
Thanks to http://blog.naver.com/techshare/100145191355 and https://gist.github.com/DalSoft/1588818 for pointing me in the right direction for how to do this. The last link on the first site contains full source code for the solution.
The question really is whether you really need to return 17k records? How are you planning to handle all the data in the browser? The users are not going to scroll through 17000 rows anyway.
A better approach is to retrieve only a "top few" records and load more as required.
You can set it in the config as others have said, or you can set in on an individual instance of the serializer like:
var js = new JavaScriptSerializer() { MaxJsonLength = int.MaxValue };
JsonResult result = Json(r);
result.MaxJsonLength = Int32.MaxValue;
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
It appears that there is no "unlimited" value. The default is 2097152 characters, which is equivalent to 4 MB of Unicode string data.
As as already been observed, 17,000 records are hard to use well in the browser. If you are presenting an aggregate view it may be much more efficient to do the aggregation on the server and transfer only a summary in the browser. For example, consider a file system brower, we only see the top of the tree, then emit further requestes as we drill down. The number of records returned in each request is comparatively small. A tree view presentation can work well for large result sets.
Just ran into this. I'm getting over 6,000 records. Just decided I'd just do some paging. As in, I accept a page number in my MVC JsonResult endpoint, which is defaulted to 0 so it's not necessary, like so:
public JsonResult MyObjects(int pageNumber = 0)
Then instead of saying:
return Json(_repository.MyObjects.ToList(), JsonRequestBehavior.AllowGet);
I say:
return Json(_repository.MyObjects.OrderBy(obj => obj.ID).Skip(1000 * pageNumber).Take(1000).ToList(), JsonRequestBehavior.AllowGet);
It's very simple. Then, in JavaScript, instead of this:
function myAJAXCallback(items) {
// Do stuff here
}
I instead say:
var pageNumber = 0;
function myAJAXCallback(items) {
if(items.length == 1000)
// Call same endpoint but add this to the end: '?pageNumber=' + ++pageNumber
}
// Do stuff here
}
And append your records to whatever you were doing with them in the first place. Or just wait until all the calls finish and cobble the results together.
I solved the problem adding this code:
String confString = HttpContext.Current.Request.ApplicationPath.ToString();
Configuration conf = WebConfigurationManager.OpenWebConfiguration(confString);
ScriptingJsonSerializationSection section = (ScriptingJsonSerializationSection)conf.GetSection("system.web.extensions/scripting/webServices/jsonSerialization");
section.MaxJsonLength = 6553600;
conf.Save();
Solution for WebForms UpdatePanel:
Add a setting to Web.config:
<configuration>
<appSettings>
<add key="aspnet:UpdatePanelMaxScriptLength" value="2147483647" />
</appSettings>
</configuration>
https://support.microsoft.com/en-us/kb/981884
ScriptRegistrationManager class contains following code:
// Serialize the attributes to JSON and write them out
JavaScriptSerializer serializer = new JavaScriptSerializer();
// Dev10# 877767 - Allow configurable UpdatePanel script block length
// The default is JavaScriptSerializer.DefaultMaxJsonLength
if (AppSettings.UpdatePanelMaxScriptLength > 0) {
serializer.MaxJsonLength = AppSettings.UpdatePanelMaxScriptLength;
}
string attrText = serializer.Serialize(attrs);
We don't need any server side changes. you can fix this only modify by web.config file
This helped for me. try this out
<appSettings>
<add key="aspnet:MaxJsonDeserializerMembers" value="2147483647" />
<add key="aspnet:UpdatePanelMaxScriptLength" value="2147483647" />
</appSettings>
and
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647"/>
</webServices>
</scripting>
i use this and it worked for Kendo grid read request.
{
//something
var result = XResult.ToList().ToDataSourceResult(request);
var rs = Json(result, JsonRequestBehavior.AllowGet);
rs.MaxJsonLength = int.MaxValue;
return rs;
}
use lib\Newtonsoft.Json.dll
public string serializeObj(dynamic json) {
return JsonConvert.SerializeObject(json);
}
if this maxJsonLength value is a int then how big is its int 32bit/64bit/16bit.... i just want to be sure whats the maximum value i can set as my maxJsonLength
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647">
</jsonSerialization>
</webServices>
</scripting>
You do not need to do with web.config
You can use short property during catch value of the passing list
For example
declare a model like
public class BookModel
{
public decimal id { get; set; } // 1
public string BN { get; set; } // 2 Book Name
public string BC { get; set; } // 3 Bar Code Number
public string BE { get; set; } // 4 Edition Name
public string BAL { get; set; } // 5 Academic Level
public string BCAT { get; set; } // 6 Category
}
here i use short proporties like
BC =barcode
BE=book edition and so on

Function returning string instead of json object

I am working on/extending TOH. I have an add method that wants to push the new Hero onto the array then return user to the list.
the result of the call to addHero is coming back as a JSON hero inside double ticks, so as a string.
i have read many articles which seem to point to
angular.toJson
or
.map(response=> response.JSON())
These are not working.
Here is the excerpt from my heroes.component.ts
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero =>
{
this.heroes.push(hero);
this.location.go('/Heroes');
}
);
}
and hero.service.ts
This clearly returns a Hero object...
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
tap((hero: Hero) => console.log(`added hero w/ id=${hero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
Upon return, the result is successfully pushed to the array, but enclosed in quotes.
The UI evidence of this is that the list has a blank entry at the bottom because there is no .Name property on that final array element.
If i refresh the page, everyone gets loaded as json.
Simple question but I can not seem to find an answer. Have been through many S/O questions involving ng2, php, etc. but none seem to address ng6, or provide a clue I can take away. if they do, Im missing it.
If you want to parse a json into a javascript object you can use JSON.parse(response.JSON())
I'm guessing response.JSON() is returning the response as json, which then needs to be parsed
Edit: you can probably just remove the map => response.JSON() . As angular httpClient already parses it
The issue was the return type of the routine in the service.
#youris question got me to look harder at the service.
The issue is that the default response is not coded correctly as utf8 and 'text/html'
Here is the corrected code.
Public Function add(mh As mHero) As CustomJsonStringResult
Dim h As New TOHbos.Hero()
h.name.Value = mh.name
h.Save()
Return JsonStringResultExtension.JSONString(Me, h.JSON, HttpStatusCode.OK)
End Function
the referenced JsonStringResultExtension is credited to #NKosi in this post. (Code included for ease of referece)
Web Api: recommended way to return json string
public static class JsonStringResultExtension {
public static CustomJsonStringResult JsonString(this ApiController controller, string jsonContent, HttpStatusCode statusCode = HttpStatusCode.OK) {
var result = new CustomJsonStringResult(controller.Request, statusCode, jsonContent);
return result;
}
public class CustomJsonStringResult : IHttpActionResult {
private string json;
private HttpStatusCode statusCode;
private HttpRequestMessage request;
public CustomJsonStringResult(HttpRequestMessage httpRequestMessage, HttpStatusCode statusCode = HttpStatusCode.OK, string json = "") {
this.request = httpRequestMessage;
this.json = json;
this.statusCode = statusCode;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
return Task.FromResult(Execute());
}
private HttpResponseMessage Execute() {
var response = request.CreateResponse(statusCode);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
}
}
Making these changes, the result is json, its pushed to the array correctly, and shows up in the Heroes list correctly.

How could I return a Spring MVC ModelAndView as String into a JSONResponse object?

What I want to do:
An user is prompted with a list of choices of actions to take, for example, Search User, Register New User, and so on. My idea is that when an user click pick one of the choices, an AJAX call is triggered using JQuery and then the method is executed, returning a JSONResponse.
A JSONResponse is just a simple object with 2 variables: status as String, and result as Object, as following:
public class JSONResponse {
private String status = null;
private Object result = null;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
However, sometimes I want to return a ModelAndView into this response. For example: when the user search for a user, the application searches for it and then return a ModelAndView with a list of possible users. One way to achieve this is to render a ModelAndView as String and then put it as result into a JSONResponse object. The problem is that how can I render a ModelAndView as String to put it into the JSONReponse?
PS: I'm using Tomcat 7, JDK 6, Spring MVC 3.0 with Tiles 2, Jackson (to convert java objects to JSON)
You can return ModelAndView if needed else write to a response stream.
public ModelAndView getSomething(HttpServletResponse response) {
// perform your logic
if(obj returned must be a json) {
try {
new MappingJacksonHttpMessageConverter().write(obj, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
} catch(Exception e) {
logger.error("Error while serializing to JSON string.");
}
return null;
} else {
return new ModelAndView("yourview");
}
}
From your question what I understand is sometimes you need to return json and sometimes model and view because you are passing search list using model and view.
Ideally these two things must be handled in different method but as you want to handle it in same method then your method must return String instead of ModelAndView so that you can return json data. And search list which you want to send you can put it into session and then you will get it on JSP page (because of Session Scope).
Method will look somewhat like this :
public #ResponseBody
String methodName(HttpServletRequest request) {
if(jsondata){ //Wants to send json data
return jsonFinalData.toString();
}else{
//store search list in session
return view_name;
}

WCF restful returning JSON by using Entity Framework Complex

recently I have set up a WCF restful service with EF4.
It all worked out when returning XML format response. however when it comes to JSON, I got 504 Error. unable to return json data, WCF Resful Service .NET 4.0
By digging deeper by using Service Trace Viewer:
I found this error:
'The type 'xxx.DataEntity.AppView'
cannot be serialized to JSON because
its IsReference setting is 'True'. The
JSON format does not support
references because there is no
standardized format for representing
references. To enable serialization,
disable the IsReference setting on the
type or an appropriate parent class of
the type.'
The "AppView" is a complex object class which generated by EF4 from a store procedure.
I spend quite a bit time google how to disable the IsReference, very little result so far.
anyone? with any solutions?
thanks in advance
Code:
[OperationContract]
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "App/{id}/{format}")]
AppView FuncDetail(string id, string format);
public AppView FuncDetail(string id, string format)
{
SetResponseFormat(format);
return AppSvcs.GetById(id);
}
private void SetResponseFormat(string format)
{
if (format.ToLower() == "json")
{
ResponseContext.Format = WebMessageFormat.Json;
}
else
{
ResponseContext.Format = WebMessageFormat.Xml;
}
}
I ran into exactly the same issue. It only happened on one of my service methods where I was trying to return JSON serialised Entity objects. For all my other methods I was returning JSON serialised data transfer objects (DTOs), which are stand-alone and not connected to the Entity framework. I am using DTOs for data posted into methods. Often, the data you send out does not need all the data you store in the model or the database e.g. ID values, updated dates, etc. The mapping is done in the model class, like so:
public partial class Location
{
public static LocationDto CreateLocationDto(Location location)
{
LocationDto dto = new LocationDto
{
Accuracy = location.Accuracy,
Altitude = location.Altitude,
Bearing = location.Bearing
};
return dto;
}
It may seem a bit clunky but it works and it ensures that you only send the data fields you intended to send back. It works for me because I only have 5 or 6 entities but I can see that it would get a bit tedious if you have lots of classes.
I was running into the same problem, as caused by using the auto-generated ADO Entity Models. I have not found a direct fix for this issue, but as a work around, I serialize the response as json explicitly.
So in your example, the AppView FuncDetail looks like this:
public object FuncDetail(string id, string format)
{
SetResponseFormat(format);
// where AppSvc is the object type and the enumerable list of this type is returned by the GetById method, cast it to a json string
return JSONSerializer.ToJson<AppSvc>(AppSvcs.GetById(id));
}
Here are the Serializers that I'm using:
public static class GenericSerializer
{
public static DataTable ToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
}
public static class JSONSerializer
{
public static string ToJson<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = GenericSerializer.ToDataTable(varlist);
return GetJSONString(dtReturn);
}
static object RowsToDictionary(this DataTable table)
{
var columns = table.Columns.Cast<DataColumn>().ToArray();
return table.Rows.Cast<DataRow>().Select(r => columns.ToDictionary(c => c.ColumnName, c => r[c]));
}
static Dictionary<string, object> ToDictionary(this DataTable table)
{
return new Dictionary<string, object>
{
{ table.TableName, table.RowsToDictionary() }
};
}
static Dictionary<string, object> ToDictionary(this DataSet data)
{
return data.Tables.Cast<DataTable>().ToDictionary(t => "Table", t => t.RowsToDictionary());
}
public static string GetJSONString(DataTable table)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(table.ToDictionary());
}
public static string GetJSONString(DataSet data)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(data.ToDictionary());
}}
It is a lot clearer to use Entity Metadata instead of Reflection.
The Metadata is pretty extensive.
another way to do this is to use LINQ to create an anonymous type with the subset of fields that you need from your entity and then use JSON.NET to serialize the collection of anon types that you created in the LINQ statement. then persist that collection out as a string by serializing.

ASP.net MVC returning JSONP

I am looking to return some JSON across domains and I understand that the way to do this is through JSONP rather than pure JSON.
I am using ASP.net MVC so I was thinking about just extending the JsonResult type and then extending the Controller so that it also implemented a Jsonp method.
Is this the best way to go about it or is there a built-in ActionResult that might be better?
Solution: I went ahead and did that. Just for reference sake I added a new result:
public class JsonpResult : System.Web.Mvc.JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/javascript";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
HttpRequestBase request = context.HttpContext.Request;
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(request.Params["jsoncallback"] + "(" + serializer.Serialize(Data) + ")");
#pragma warning restore 0618
}
}
}
and also a couple of methods to a superclass of all my controllers:
protected internal JsonpResult Jsonp(object data)
{
return Jsonp(data, null /* contentType */);
}
protected internal JsonpResult Jsonp(object data, string contentType)
{
return Jsonp(data, contentType, null);
}
protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding)
{
return new JsonpResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding
};
}
Works like a charm.
Here is a simple solution, if you don't want to define an action filter
Client side code using jQuery:
$.ajax("http://www.myserver.com/Home/JsonpCall", { dataType: "jsonp" }).done(function (result) {});
MVC controller action. Returns content result with JavaScript code executing callback function provided with query string. Also sets JavaScript MIME type for response.
public ContentResult JsonpCall(string callback)
{
return Content(String.Format("{0}({1});",
callback,
new JavaScriptSerializer().Serialize(new { a = 1 })),
"application/javascript");
}
Rather than subclassing my controllers with Jsonp() methods, I went the extension method route as it feels a touch cleaner to me. The nice thing about the JsonpResult is that you can test it exactly the same way you would a JsonResult.
I did:
public static class JsonResultExtensions
{
public static JsonpResult ToJsonp(this JsonResult json)
{
return new JsonpResult { ContentEncoding = json.ContentEncoding, ContentType = json.ContentType, Data = json.Data, JsonRequestBehavior = json.JsonRequestBehavior};
}
}
This way you don't have to worry about creating all the different Jsonp() overloads, just convert your JsonResult to a Jsonp one.
Ranju's blog post (aka "This blog post I found") is excellent, and reading it will allow you to further the solution below so that your controller can handle same-domain JSON and cross-domain JSONP requests elegantly in the same controller action without additional code [in the action].
Regardless, for the "give me the code" types, here it is, in case the blog disappears again.
In your controller (this snippet is new/non-blog code):
[AllowCrossSiteJson]
public ActionResult JsonpTime(string callback)
{
string msg = DateTime.UtcNow.ToString("o");
return new JsonpResult
{
Data = (new
{
time = msg
})
};
}
JsonpResult found on
this excellent blog post:
/// <summary>
/// Renders result as JSON and also wraps the JSON in a call
/// to the callback function specified in "JsonpResult.Callback".
/// http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx
/// </summary>
public class JsonpResult : JsonResult
{
/// <summary>
/// Gets or sets the javascript callback function that is
/// to be invoked in the resulting script output.
/// </summary>
/// <value>The callback function name.</value>
public string Callback { get; set; }
/// <summary>
/// Enables processing of the result of an action method by a
/// custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
/// </summary>
/// <param name="context">The context within which the
/// result is executed.</param>
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
response.ContentType = ContentType;
else
response.ContentType = "application/javascript";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Callback == null || Callback.Length == 0)
Callback = context.HttpContext.Request.QueryString["callback"];
if (Data != null)
{
// The JavaScriptSerializer type was marked as obsolete
// prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
JavaScriptSerializer serializer = new JavaScriptSerializer();
string ser = serializer.Serialize(Data);
response.Write(Callback + "(" + ser + ");");
#pragma warning restore 0618
}
}
}
Note: Following up on the comments to the OP by #Ranju and others, I figured it was worth posting the "bare minimum" functional code from Ranju's blog post as a community wiki. Though it's safe to say that Ranju added the above and other code on his blog to be used freely, I'm not going to copy his words here.
For ASP.NET Core ,NOT ASP.NET MVC
This is a tailored version for ASP.NET CORE of the solution which exists in the answer
public class JsonpResult : JsonResult
{
public JsonpResult(object value) : base(value)
{
}
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
HttpResponse response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
response.ContentType = ContentType;
else
response.ContentType = "application/javascript";
if (Value != null)
{
HttpRequest request = context.HttpContext.Request;
string serializedJson = JsonConvert.SerializeObject(Value);
string result = $"{request.Query["callback"]}({serializedJson})";
await response.WriteAsync(result);
}
}
}
The referenced articles by stimms and ranju v were both very useful and made the situation clear.
However, I was left scratching my head about using extensions, sub-classing in context of the MVC code I had found online.
There was two key points that caught me out:
The code I had derived from ActionResult, but in ExecuteResult there was some code to return either XML or JSON.
I had then created a Generics based ActionResult, to ensure the same ExecuteResults was used independant of the type of data I returned.
So, combining the two - I did not need further extensions or sub-classing to add the mechanism to return JSONP, simply change my existing ExecuteResults.
What had confused me is that really I was looking for a way to derive or extend JsonResult, without re-coding the ExecuteResult. As JSONP is effectively a JSON string with prefix & suffix it seemed a waste. However the underling ExecuteResult uses respone.write - so the safest way of changing is to re-code ExecuteResults as handily provided by various postings!
I can post some code if that would be useful, but there is quite a lot of code in this thread already.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace Template.Web.Helpers
{
public class JsonpResult : JsonResult
{
public JsonpResult(string callbackName)
{
CallbackName = callbackName;
}
public JsonpResult()
: this("jsoncallback")
{
}
public string CallbackName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;
string jsoncallback = ((context.RouteData.Values[CallbackName] as string) ?? request[CallbackName]) ?? CallbackName;
if (!string.IsNullOrEmpty(jsoncallback))
{
if (string.IsNullOrEmpty(base.ContentType))
{
base.ContentType = "application/x-javascript";
}
response.Write(string.Format("{0}(", jsoncallback));
}
base.ExecuteResult(context);
if (!string.IsNullOrEmpty(jsoncallback))
{
response.Write(")");
}
}
}
public static class ControllerExtensions
{
public static JsonpResult Jsonp(this Controller controller, object data, string callbackName = "callback")
{
return new JsonpResult(callbackName)
{
Data = data,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
public static T DeserializeObject<T>(this Controller controller, string key) where T : class
{
var value = controller.HttpContext.Request.QueryString.Get(key);
if (string.IsNullOrEmpty(value))
{
return null;
}
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
return javaScriptSerializer.Deserialize<T>(value);
}
}
}
//Example of using the Jsonp function::
// 1-
public JsonResult Read()
{
IEnumerable<User> result = context.All();
return this.Jsonp(result);
}
//2-
public JsonResult Update()
{
var models = this.DeserializeObject<IEnumerable<User>>("models");
if (models != null)
{
Update(models); //Update properties & save change in database
}
return this.Jsonp(models);
}
the solution above is a good way of working but it should be extendend with a new type of result instead of having a method that returns a JsonResult you should write methods that return your own result types
public JsonPResult testMethod() {
// use the other guys code to write a method that returns something
}
public class JsonPResult : JsonResult
{
public FileUploadJsonResult(JsonResult data) {
this.Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
this.ContentType = "text/html";
context.HttpContext.Response.Write("<textarea>");
base.ExecuteResult(context);
context.HttpContext.Response.Write("</textarea>");
}
}