How to send very long json to Asp.NET MVC [duplicate] - json
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
Related
Increasing maxjsonlength on MVC post from Javascript
I have a controller action Export which accepts a List of models like below. This is sending back and manipulated dataset back from the view where the user could interact with it. So we have been able to send the data down with much more information. [HttpPost] public JsonResult Export(List<MappingExportModel> sources){} This works fine in all cases but there is one where we have a bigger than normal dataset. This is causing an issue with the export. So far I have tried just passing the values as an object or string but I am unable to convert them into any usable instance after the data is into the controller. Is it possible to preemptively increase this maxjsonlength value somewhere. The value from the web.config is being ignored from what I have come across so far. The error I receive is "Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property" I need to be able to accept this directly from the ajax request into the controller action. Spinning up a version of JsonResult and then setting the max value will not work because the error is thrown the the data is trying to be deserialized into the object var presented above. We get the value in the original GET request and do set the value before the view is loaded. Now we are taking the data from this view and sending it back plus all the manipulations the users have created. User posts data to server, the controller action is hit with the data. The error is encountered and spit back out to the browser which handles the error.
You can use custom json length. add the following file in your project and edit your global.asax.cs Global.asax public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); ///// ********** JsonValueProviderFactory jsonValueProviderFactory = null; foreach (var factory in ValueProviderFactories.Factories) { if (factory is JsonValueProviderFactory) { jsonValueProviderFactory = factory as JsonValueProviderFactory; } } //remove the default JsonVAlueProviderFactory if (jsonValueProviderFactory != null) ValueProviderFactories.Factories.Remove(jsonValueProviderFactory); //add the custom one ValueProviderFactories.Factories.Add(new CustomJsonValueProviderFactory()); /////************* } } ///******** for json length public sealed class CustomJsonValueProviderFactory : ValueProviderFactory { private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value) { IDictionary<string, object> d = value as IDictionary<string, object>; if (d != null) { foreach (KeyValuePair<string, object> entry in d) { AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value); } return; } IList l = value as IList; if (l != null) { for (int i = 0; i < l.Count; i++) { AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]); } return; } // primitive backingStore[prefix] = value; } private static object GetDeserializedObject(ControllerContext controllerContext) { if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { // not JSON request return null; } StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); string bodyText = reader.ReadToEnd(); if (String.IsNullOrEmpty(bodyText)) { // no JSON data return null; } JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.MaxJsonLength = int.MaxValue; //increase MaxJsonLength. This could be read in from the web.config if you prefer object jsonData = serializer.DeserializeObject(bodyText); return jsonData; } public override IValueProvider GetValueProvider(ControllerContext controllerContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } object jsonData = GetDeserializedObject(controllerContext); if (jsonData == null) { return null; } Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); AddToBackingStore(backingStore, String.Empty, jsonData); return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture); } private static string MakeArrayKey(string prefix, int index) { return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]"; } private static string MakePropertyKey(string prefix, string propertyName) { return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName; } } JsonValueProviderFactory.cs public sealed class JsonValueProviderFactory : ValueProviderFactory { private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value) { IDictionary<string, object> d = value as IDictionary<string, object>; if (d != null) { foreach (KeyValuePair<string, object> entry in d) { AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value); } return; } IList l = value as IList; if (l != null) { for (int i = 0; i < l.Count; i++) { AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]); } return; } // primitive backingStore[prefix] = value; } private static object GetDeserializedObject(ControllerContext controllerContext) { if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { // not JSON request return null; } StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); string bodyText = reader.ReadToEnd(); if (String.IsNullOrEmpty(bodyText)) { // no JSON data return null; } JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.MaxJsonLength = int.MaxValue; //increase MaxJsonLength. This could be read in from the web.config if you prefer object jsonData = serializer.DeserializeObject(bodyText); return jsonData; } public override IValueProvider GetValueProvider(ControllerContext controllerContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } object jsonData = GetDeserializedObject(controllerContext); if (jsonData == null) { return null; } Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); AddToBackingStore(backingStore, String.Empty, jsonData); return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture); } private static string MakeArrayKey(string prefix, int index) { return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]"; } private static string MakePropertyKey(string prefix, string propertyName) { return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName; } } by this you can pass lengthy json through ajax to controller and if you want to retrieve a lengthy string back to ajax result from controller then add below code in your controller also //add this for getting large json string 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 }; }
Controller.Json function slow for IQueryable
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()); } } }
The length of the string exceeds the value set on the maxJsonLength property. in MVC3
MVC3 (.cshtml File) $.getJSON(URL, Data, function (data) { document.getElementById('divDisplayMap').innerHTML = data; if (data != null) { $('#myTablesId').show(); tdOnclickEvent(); } else { $('#myTablesId').hide(); } }).error(function (xhr, ajaxOptions, thrownError) { debugger; }); on Server Side public JsonResult ZoneType_SelectedState(int x_Id, int y_Id) { JsonResult result = new JsonResult(); result.Data = "LongString";//Longstring with the length mention below return Json(result.Data,"application/json", JsonRequestBehavior.AllowGet); } from the server side i am passing the string with the length of 1194812 and more than that. but i am getting the error saying the "Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property." Please help me out ASP
You could write a custom ActionResult which will allow you to specify the maximum length of data that the serializer can handle: public class MyJsonResult : ActionResult { private readonly object data; public MyJsonResult(object data) { this.data = data; } public override void ExecuteResult(ControllerContext context) { var response = context.RequestContext.HttpContext.Response; response.ContentType = "application/json"; var serializer = new JavaScriptSerializer(); // You could set the MaxJsonLength to the desired size - 10MB in this example serializer.MaxJsonLength = 10 * 1024 * 1024; response.Write(serializer.Serialize(this.data)); } } and then use it: public ActionResult ZoneType_SelectedState(int x_Id, int y_Id) { string data = "LongString";//Longstring with the length mention below; return new MyJsonResult(data); }
Try to update your Controller method as shown below: public JsonResult ZoneType_SelectedState(int x_Id, int y_Id) { var result = Json("LongString", JsonRequestBehavior.AllowGet); result.MaxJsonLength = int.MaxValue; return result; }
How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?
I have a simple key/value list in JSON being sent back to ASP.NET via POST. Example: { "key1": "value1", "key2": "value2"} I AM NOT TRYING TO DESERIALIZE INTO STRONGLY-TYPED .NET OBJECTS I simply need a plain old Dictionary(Of String, String), or some equivalent (hash table, Dictionary(Of String, Object), old-school StringDictionary--hell, a 2-D array of strings would work for me. I can use anything available in ASP.NET 3.5, as well as the popular Json.NET (which I'm already using for serialization to the client). Apparently neither of these JSON libraries have this forehead-slapping obvious capability out of the box--they are totally focused on reflection-based deserialization via strong contracts. Any ideas? Limitations: I don't want to implement my own JSON parser Can't use ASP.NET 4.0 yet Would prefer to stay away from the older, deprecated ASP.NET class for JSON
Json.NET does this... string json = #"{""key1"":""value1"",""key2"":""value2""}"; var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json); More examples: Serializing Collections with Json.NET
I did discover .NET has a built in way to cast the JSON string into a Dictionary<String, Object> via the System.Web.Script.Serialization.JavaScriptSerializer type in the 3.5 System.Web.Extensions assembly. Use the method DeserializeObject(String). I stumbled upon this when doing an ajax post (via jquery) of content type 'application/json' to a static .net Page Method and saw that the method (which had a single parameter of type Object) magically received this Dictionary.
System.Text.Json This can now be done using System.Text.Json which is built-in to .NET Core 3.0. It's now possible to deserialize JSON without using third-party libraries. var json = #"{""key1"":""value1"",""key2"":""value2""}"; var values = JsonSerializer.Deserialize<Dictionary<string, string>>(json); Also available in NuGet package System.Text.Json if using .NET Standard or .NET Framework. Make sure to read and understand: https://github.com/dotnet/runtime/issues/30452
For those searching the internet and stumbling upon this post, I wrote a blog post on how to use the JavaScriptSerializer class. Read more... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/ Here is an example: var json = "{\"id\":\"13\", \"value\": true}"; var jss = new JavaScriptSerializer(); var table = jss.Deserialize<dynamic>(json); Console.WriteLine(table["id"]); Console.WriteLine(table["value"]);
I had the same problem, so I wrote this my self. This solution is differentiated from other answers because it can deserialize in to multiple levels. Just send JSON string in to deserializeToDictionary function it will return non strongly-typed Dictionary<string, object> object. Old code private Dictionary<string, object> deserializeToDictionary(string jo) { var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo); var values2 = new Dictionary<string, object>(); foreach (KeyValuePair<string, object> d in values) { // if (d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject")) if (d.Value is JObject) { values2.Add(d.Key, deserializeToDictionary(d.Value.ToString())); } else { values2.Add(d.Key, d.Value); } } return values2; } Ex: This will return Dictionary<string, object> object of a Facebook JSON response. Test private void button1_Click(object sender, EventArgs e) { string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\", hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}"; Dictionary<string, object> values = deserializeToDictionary(responsestring); } Note: hometown further deserilize into a Dictionary<string, object> object. Update My old answer works great if there is no array on JSON string. This one further deserialize in to a List<object> if an element is an array. Just send a JSON string in to deserializeToDictionaryOrList function it will return non strongly-typed Dictionary<string, object> object or List<object>. private static object deserializeToDictionaryOrList(string jo,bool isArray=false) { if (!isArray) { isArray = jo.Substring(0, 1) == "["; } if (!isArray) { var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo); var values2 = new Dictionary<string, object>(); foreach (KeyValuePair<string, object> d in values) { if (d.Value is JObject) { values2.Add(d.Key, deserializeToDictionary(d.Value.ToString())); } else if (d.Value is JArray) { values2.Add(d.Key, deserializeToDictionary(d.Value.ToString(), true)); } else { values2.Add(d.Key, d.Value); } } return values2; }else { var values = JsonConvert.DeserializeObject<List<object>>(jo); var values2 = new List<object>(); foreach (var d in values) { if (d is JObject) { values2.Add(deserializeToDictionary(d.ToString())); } else if (d is JArray) { values2.Add(deserializeToDictionary(d.ToString(), true)); } else { values2.Add(d); } } return values2; } }
Tried to not use any external JSON implementation so i deserialised like this: string json = "{\"id\":\"13\", \"value\": true}"; var serializer = new JavaScriptSerializer(); //using System.Web.Script.Serialization; Dictionary<string, string> values = serializer.Deserialize<Dictionary<string, string>>(json);
I just needed to parse a nested dictionary, like { "x": { "a": 1, "b": 2, "c": 3 } } where JsonConvert.DeserializeObject doesn't help. I found the following approach: var dict = JObject.Parse(json).SelectToken("x").ToObject<Dictionary<string, int>>(); The SelectToken lets you dig down to the desired field. You can even specify a path like "x.y.z" to step further down into the JSON object.
If you're after a lightweight, no-added-references kind of approach, maybe this bit of code I just wrote will work (I can't 100% guarantee robustness though). using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; public Dictionary<string, object> ParseJSON(string json) { int end; return ParseJSON(json, 0, out end); } private Dictionary<string, object> ParseJSON(string json, int start, out int end) { Dictionary<string, object> dict = new Dictionary<string, object>(); bool escbegin = false; bool escend = false; bool inquotes = false; string key = null; int cend; StringBuilder sb = new StringBuilder(); Dictionary<string, object> child = null; List<object> arraylist = null; Regex regex = new Regex(#"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase); int autoKey = 0; for (int i = start; i < json.Length; i++) { char c = json[i]; if (c == '\\') escbegin = !escbegin; if (!escbegin) { if (c == '"') { inquotes = !inquotes; if (!inquotes && arraylist != null) { arraylist.Add(DecodeString(regex, sb.ToString())); sb.Length = 0; } continue; } if (!inquotes) { switch (c) { case '{': if (i != start) { child = ParseJSON(json, i, out cend); if (arraylist != null) arraylist.Add(child); else { dict.Add(key, child); key = null; } i = cend; } continue; case '}': end = i; if (key != null) { if (arraylist != null) dict.Add(key, arraylist); else dict.Add(key, DecodeString(regex, sb.ToString())); } return dict; case '[': arraylist = new List<object>(); continue; case ']': if (key == null) { key = "array" + autoKey.ToString(); autoKey++; } if (arraylist != null && sb.Length > 0) { arraylist.Add(sb.ToString()); sb.Length = 0; } dict.Add(key, arraylist); arraylist = null; key = null; continue; case ',': if (arraylist == null && key != null) { dict.Add(key, DecodeString(regex, sb.ToString())); key = null; sb.Length = 0; } if (arraylist != null && sb.Length > 0) { arraylist.Add(sb.ToString()); sb.Length = 0; } continue; case ':': key = DecodeString(regex, sb.ToString()); sb.Length = 0; continue; } } } sb.Append(c); if (escend) escbegin = false; if (escbegin) escend = true; else escend = false; } end = json.Length - 1; return dict; //theoretically shouldn't ever get here } private string DecodeString(Regex regex, string str) { return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber)))); } [I realise that this violates the OP Limitation #1, but technically, you didn't write it, I did]
Mark Rendle posted this as a comment, I wanted to post it as an answer since it's the only solution that has worked so far to return the success and the error-codes json results from the Google reCaptcha response. string jsonReponseString= wClient.DownloadString(requestUrl); IDictionary<string, object> dict = new JavaScriptSerializer().DeserializeObject(jsonReponseString) as IDictionary<string, object>; Thanks again, Mark!
For anyone who is trying to convert JSON to dictionary just for retrieving some value out of it. There is a simple way using Newtonsoft.JSON using Newtonsoft.Json.Linq ... JObject o = JObject.Parse(#"{ 'CPU': 'Intel', 'Drives': [ 'DVD read/writer', '500 gigabyte hard drive' ] }"); string cpu = (string)o["CPU"]; // Intel string firstDrive = (string)o["Drives"][0]; // DVD read/writer IList<string> allDrives = o["Drives"].Select(t => (string)t).ToList(); // DVD read/writer // 500 gigabyte hard drive
I've added upon the code submitted by jSnake04 and Dasun herein. I've added code to create lists of objects from JArray instances. It has two-way recursion but as it is functioning on a fixed, finite tree model, there is no risk of stack overflow unless the data is massive. /// <summary> /// Deserialize the given JSON string data (<paramref name="data"/>) into a /// dictionary. /// </summary> /// <param name="data">JSON string.</param> /// <returns>Deserialized dictionary.</returns> private IDictionary<string, object> DeserializeData(string data) { var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(data); return DeserializeData(values); } /// <summary> /// Deserialize the given JSON object (<paramref name="data"/>) into a dictionary. /// </summary> /// <param name="data">JSON object.</param> /// <returns>Deserialized dictionary.</returns> private IDictionary<string, object> DeserializeData(JObject data) { var dict = data.ToObject<Dictionary<String, Object>>(); return DeserializeData(dict); } /// <summary> /// Deserialize any elements of the given data dictionary (<paramref name="data"/>) /// that are JSON object or JSON arrays into dictionaries or lists respectively. /// </summary> /// <param name="data">Data dictionary.</param> /// <returns>Deserialized dictionary.</returns> private IDictionary<string, object> DeserializeData(IDictionary<string, object> data) { foreach (var key in data.Keys.ToArray()) { var value = data[key]; if (value is JObject) data[key] = DeserializeData(value as JObject); if (value is JArray) data[key] = DeserializeData(value as JArray); } return data; } /// <summary> /// Deserialize the given JSON array (<paramref name="data"/>) into a list. /// </summary> /// <param name="data">Data dictionary.</param> /// <returns>Deserialized list.</returns> private IList<Object> DeserializeData(JArray data) { var list = data.ToObject<List<Object>>(); for (int i = 0; i < list.Count; i++) { var value = list[i]; if (value is JObject) list[i] = DeserializeData(value as JObject); if (value is JArray) list[i] = DeserializeData(value as JArray); } return list; }
Edit: This works, but the accepted answer using Json.NET is much more straightforward. Leaving this one in case someone needs BCL-only code. It’s not supported by the .NET framework out of the box. A glaring oversight – not everyone needs to deserialize into objects with named properties. So I ended up rolling my own: VB.NET: <Serializable()> Public Class StringStringDictionary Implements ISerializable Public dict As System.Collections.Generic.Dictionary(Of String, String) Public Sub New() dict = New System.Collections.Generic.Dictionary(Of String, String) End Sub Protected Sub New(info As SerializationInfo, _ context As StreamingContext) dict = New System.Collections.Generic.Dictionary(Of String, String) For Each entry As SerializationEntry In info dict.Add(entry.Name, DirectCast(entry.Value, String)) Next End Sub Public Sub GetObjectData(info As SerializationInfo, context As StreamingContext) Implements ISerializable.GetObjectData For Each key As String in dict.Keys info.AddValue(key, dict.Item(key)) Next End Sub End Class same on C#: public class StringStringDictionary : ISerializable { public System.Collections.Generic.Dictionary<string, string> dict; public StringStringDictionary() { dict = new System.Collections.Generic.Dictionary<string, string>(); } protected StringStringDictionary(SerializationInfo info, StreamingContext context) { dict = new System.Collections.Generic.Dictionary<string, string>(); foreach (SerializationEntry entry in info) dict.Add(entry.Name, (string)entry.Value); } public void GetObjectData(SerializationInfo info, StreamingContext context) { foreach (string key in dict.Keys) info.AddValue(key, dict[key]); } } Called with: string MyJsonString = "{ \"key1\": \"value1\", \"key2\": \"value2\"}"; System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer( typeof(StringStringDictionary)); System.IO.MemoryStream ms = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(MyJsonString)); StringStringDictionary myfields = (StringStringDictionary)dcjs.ReadObject(ms); Response.Write("Value of key2: " + myfields.dict["key2"]); Sorry for the mix of C# and VB.NET…
I added a check for null values in the JSON to the other answer I had same problem so I wrote this my self. This solution is differentiated from other answers because it can deserialize in to multiple levels. Just send json string in to deserializeToDictionary function it will return non strongly-typed Dictionary<string, object> object. private Dictionary<string, object> deserializeToDictionary(string jo) { var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo); var values2 = new Dictionary<string, object>(); foreach (KeyValuePair<string, object> d in values) { if (d.Value != null && d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject")) { values2.Add(d.Key, deserializeToDictionary(d.Value.ToString())); } else { values2.Add(d.Key, d.Value); } } return values2; } Ex: This will return Dictionary<string, object> object of a Facebook JSON response. private void button1_Click(object sender, EventArgs e) { string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\", hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}"; Dictionary<string, object> values = deserializeToDictionary(responsestring); } Note: hometown further deserialize into a Dictionary<string, object> object.
It seems all of these answers here just assume you can get that little string out of a bigger object... for people looking to simply deserealize a large object with such a dictionary somewhere inside the mapping, and who are using the System.Runtime.Serialization.Json DataContract system, here's a solution: An answer on gis.stackexchange.com had this interesting link. I had to recover it with archive.org, but it offers a pretty much perfect solution: a custom IDataContractSurrogate class in which you implement exactly your own types. I was able to expand it easily. I made a bunch of changes in it, though. Since the original source is no longer available, I'll post the entire class here: using System; using System.CodeDom; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; namespace JsonTools { /// <summary> /// Allows using Dictionary<String,String> and Dictionary<String,Boolean> types, and any others you'd like to add. /// Source: https://web.archive.org/web/20100317222656/my6solutions.com/post/2009/06/30/DataContractSerializer-DataContractJsonSerializer-JavaScriptSerializer-XmlSerializer-for-serialization.aspx /// </summary> public class JsonSurrogate : IDataContractSurrogate { /// <summary> /// Deserialize an object with added support for the types defined in this class. /// </summary> /// <typeparam name="T">Contract class</typeparam> /// <param name="json">JSON String</param> /// <param name="encoding">Text encoding</param> /// <returns>The deserialized object of type T</returns> public static T Deserialize<T>(String json, Encoding encoding) { if (encoding == null) encoding = new UTF8Encoding(false); DataContractJsonSerializer deserializer = new DataContractJsonSerializer( typeof(T), new Type[0], int.MaxValue, true, new JsonSurrogate(), false); using (MemoryStream stream = new MemoryStream(encoding.GetBytes(json))) { T result = (T)deserializer.ReadObject(stream); return result; } } // make sure all values in this are classes implementing JsonSurrogateObject. private static Dictionary<Type, Type> KnownTypes = new Dictionary<Type, Type>() { {typeof(Dictionary<String, String>), typeof(SSDictionary)}, {typeof(Dictionary<String, Boolean>), typeof(SBDictionary)} }; #region Implemented surrogate dictionary classes [Serializable] public class SSDictionary : SurrogateDictionary<String> { public SSDictionary() : base() {} protected SSDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {} } [Serializable] public class SBDictionary : SurrogateDictionary<Boolean> { public SBDictionary() : base() {} protected SBDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {} } #endregion /// <summary>Small interface to easily extract the final value from the object.</summary> public interface JsonSurrogateObject { Object DeserializedObject { get; } } /// <summary> /// Class for deserializing any simple dictionary types with a string as key. /// </summary> /// <typeparam name="T">Any simple type that will be deserialized correctly.</typeparam> [Serializable] public abstract class SurrogateDictionary<T> : ISerializable, JsonSurrogateObject { public Object DeserializedObject { get { return dict; } } private Dictionary<String, T> dict; public SurrogateDictionary() { dict = new Dictionary<String, T>(); } // deserialize protected SurrogateDictionary(SerializationInfo info, StreamingContext context) { dict = new Dictionary<String, T>(); foreach (SerializationEntry entry in info) { // This cast will only work for base types, of course. dict.Add(entry.Name, (T)entry.Value); } } // serialize public void GetObjectData(SerializationInfo info, StreamingContext context) { foreach (String key in dict.Keys) { info.AddValue(key, dict[key]); } } } /// <summary> /// Uses the KnownTypes dictionary to get the surrogate classes. /// </summary> /// <param name="type"></param> /// <returns></returns> public Type GetDataContractType(Type type) { Type returnType; if (KnownTypes.TryGetValue(type, out returnType)) { return returnType; } return type; } public object GetObjectToSerialize(object obj, Type targetType) { throw new NotImplementedException(); } /// <summary> /// Gets the object out of the surrogate datacontract object. This function is the reason all surrogate objects need to implement the JsonSurrogateObject class. /// </summary> /// <param name="obj">Result of the deserialization</param> /// <param name="targetType">Expected target type of the deserialization</param> /// <returns></returns> public object GetDeserializedObject(object obj, Type targetType) { if (obj is JsonSurrogateObject) { return ((JsonSurrogateObject)obj).DeserializedObject; } return obj; } public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) { return null; } #region not implemented public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) { throw new NotImplementedException(); } public object GetCustomDataToExport(Type clrType, Type dataContractType) { throw new NotImplementedException(); } public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) { throw new NotImplementedException(); } public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) { throw new NotImplementedException(); } #endregion } } To add new supported types to the class, you just need to add your class, give it the right constructors and functions (look at SurrogateDictionary for an example), make sure it inherits JsonSurrogateObject, and add its type mapping to the KnownTypes dictionary. The included SurrogateDictionary can serve as basis for any Dictionary<String,T> types where T is any type that does deserialize correctly. Calling it is really simple: MyObjtype newObj = JsonSurrogate.Deserialize<MyObjtype>(jsonStr, encoding); Note that for some reason this thing has trouble using key strings which contain spaces; they were simply not present in the final list. Might just be it's simply against json specs and the api I was calling was poorly implemented, mind you; I dunno. Anyway, I solved this by regex-replacing them with underscores in the raw json data and fixing the dictionary after the deserialization.
Based on comments above try JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json) var json = #"{""key1"":1,""key2"":""value2"", ""object1"":{""property1"":""value1"",""property2"":[2,3,4,5,6,7]}}"; var parsedObject = JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json); seems to work even for complex objects and lists.
My approach directly deserializes to IDictionary, without JObject or ExpandObject in between. The code uses converter, which is basically copied from ExpandoObjectConverter class found in JSON.NET sourcecode, but using IDictionary instead of ExpandoObject. Usage: var settings = new JsonSerializerSettings() { Converters = { new DictionaryConverter() }, }; var result = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, settings); Code: // based on ExpandoObjectConverter, but using arrays instead of IList, to behave similar to System.Web.Script.Serialization.JavaScriptSerializer public class DictionaryConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return ReadValue(reader); } public override bool CanConvert(Type objectType) { return (objectType == typeof(IDictionary<string, object>)); } public override bool CanWrite { get { return false; } } private object ReadValue(JsonReader reader) { while (reader.TokenType == JsonToken.Comment) { if (!reader.Read()) throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>."); } switch (reader.TokenType) { case JsonToken.StartObject: return ReadObject(reader); case JsonToken.StartArray: return ReadList(reader); default: if (IsPrimitiveToken(reader.TokenType)) return reader.Value; throw JsonSerializationExceptionCreate(reader, string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting IDictionary<string, object>: {0}", reader.TokenType)); } } private object ReadList(JsonReader reader) { List<object> list = new List<object>(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.Comment: break; default: object v = ReadValue(reader); list.Add(v); break; case JsonToken.EndArray: return list; } } throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>."); } private object ReadObject(JsonReader reader) { IDictionary<string, object> dictionary = new Dictionary<string, object>(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.PropertyName: string propertyName = reader.Value.ToString(); if (!reader.Read()) throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>."); object v = ReadValue(reader); dictionary[propertyName] = v; break; case JsonToken.Comment: break; case JsonToken.EndObject: return dictionary; } } throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>."); } //based on internal Newtonsoft.Json.JsonReader.IsPrimitiveToken internal static bool IsPrimitiveToken(JsonToken token) { switch (token) { case JsonToken.Integer: case JsonToken.Float: case JsonToken.String: case JsonToken.Boolean: case JsonToken.Undefined: case JsonToken.Null: case JsonToken.Date: case JsonToken.Bytes: return true; default: return false; } } // based on internal Newtonsoft.Json.JsonSerializationException.Create private static JsonSerializationException JsonSerializationExceptionCreate(JsonReader reader, string message, Exception ex = null) { return JsonSerializationExceptionCreate(reader as IJsonLineInfo, reader.Path, message, ex); } // based on internal Newtonsoft.Json.JsonSerializationException.Create private static JsonSerializationException JsonSerializationExceptionCreate(IJsonLineInfo lineInfo, string path, string message, Exception ex) { message = JsonPositionFormatMessage(lineInfo, path, message); return new JsonSerializationException(message, ex); } // based on internal Newtonsoft.Json.JsonPosition.FormatMessage internal static string JsonPositionFormatMessage(IJsonLineInfo lineInfo, string path, string message) { if (!message.EndsWith(Environment.NewLine)) { message = message.Trim(); if (!message.EndsWith(".", StringComparison.Ordinal)) message += "."; message += " "; } message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path); if (lineInfo != null && lineInfo.HasLineInfo()) message += string.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition); message += "."; return message; } }
Annoyingly enough, if you want to use the default model binders, it looks like you will have to use numerical index values like a form POST. See the following excerpt from this article http://msdn.microsoft.com/en-us/magazine/hh781022.aspx: Though it’s somewhat counterintuitive, JSON requests have the same requirements—they, too, must adhere to the form post naming syntax. Take, for example, the JSON payload for the previous UnitPrice collection. The pure JSON array syntax for this data would be represented as: [ { "Code": "USD", "Amount": 100.00 }, { "Code": "EUR", "Amount": 73.64 } ] However, the default value providers and model binders require the data to be represented as a JSON form post: { "UnitPrice[0].Code": "USD", "UnitPrice[0].Amount": 100.00, "UnitPrice[1].Code": "EUR", "UnitPrice[1].Amount": 73.64 } The complex object collection scenario is perhaps one of the most widely problematic scenarios that developers run into because the syntax isn’t necessarily evident to all developers. However, once you learn the relatively simple syntax for posting complex collections, these scenarios become much easier to deal with.
I just implemented this in RestSharp. This post was helpful to me. Besides the code in the link, here is my code. I now get a Dictionary of results when I do something like this: var jsonClient = new RestClient(url.Host); jsonClient.AddHandler("application/json", new DynamicJsonDeserializer()); var jsonRequest = new RestRequest(url.Query, Method.GET); Dictionary<string, dynamic> response = jsonClient.Execute<JObject>(jsonRequest).Data.ToObject<Dictionary<string, dynamic>>(); Be mindful of the sort of JSON you're expecting - in my case, I was retrieving a single object with several properties. In the attached link, the author was retrieving a list.
A bit late to the game, but non of the above solutions pointed me in the direction of a pure and simple .NET, no json.net solution. So here it is, ended up being very simple. Below a full running example of how it is done with standard .NET Json serialization, the example has dictionary both in the root object and in the child objects. The golden bullet is this cat, parse the settings as second parameter to the serializer: DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings(); settings.UseSimpleDictionaryFormat = true; Full code below: using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; namespace Kipon.dk { public class JsonTest { public const string EXAMPLE = #"{ ""id"": ""some id"", ""children"": { ""f1"": { ""name"": ""name 1"", ""subs"": { ""1"": { ""name"": ""first sub"" }, ""2"": { ""name"": ""second sub"" } } }, ""f2"": { ""name"": ""name 2"", ""subs"": { ""37"": { ""name"": ""is 37 in key""} } } } } "; [DataContract] public class Root { [DataMember(Name ="id")] public string Id { get; set; } [DataMember(Name = "children")] public Dictionary<string,Child> Children { get; set; } } [DataContract] public class Child { [DataMember(Name = "name")] public string Name { get; set; } [DataMember(Name = "subs")] public Dictionary<int, Sub> Subs { get; set; } } [DataContract] public class Sub { [DataMember(Name = "name")] public string Name { get; set; } } public static void Test() { var array = System.Text.Encoding.UTF8.GetBytes(EXAMPLE); using (var mem = new System.IO.MemoryStream(array)) { mem.Seek(0, System.IO.SeekOrigin.Begin); DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings(); settings.UseSimpleDictionaryFormat = true; var ser = new DataContractJsonSerializer(typeof(Root), settings); var data = (Root)ser.ReadObject(mem); Console.WriteLine(data.Id); foreach (var childKey in data.Children.Keys) { var child = data.Children[childKey]; Console.WriteLine(" Child: " + childKey + " " + child.Name); foreach (var subKey in child.Subs.Keys) { var sub = child.Subs[subKey]; Console.WriteLine(" Sub: " + subKey + " " + sub.Name); } } } } } }
You could use Tiny-JSON string json = "{\"key1\":\"value1\", \"key2\":\"value2\"}"; IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);
I would suggest using System.Runtime.Serialization.Json that is part of .NET 4.5. [DataContract] public class Foo { [DataMember(Name = "data")] public Dictionary<string,string> Data { get; set; } } Then use it like this: var serializer = new DataContractJsonSerializer(typeof(List<Foo>)); var jsonParams = #"{""data"": [{""Key"":""foo"",""Value"":""bar""}] }"; var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonParams)); var obj = serializer.ReadObject(stream); Console.WriteLine(obj);
Here is my solution with System.Text.Json. You get a json string for the nested objects which in own turn can be converted to needed type later on. public static Dictionary<string,string> JsonToDictionary(this string json) { var objectValues = JsonSerializer.Deserialize<Dictionary<string, object>>(json); var stringValues = objectValues.Select(o => new KeyValuePair<string, string>(o.Key, o.Value?.ToString())); return stringValues.ToDictionary(pair => pair.Key, pair => pair.Value); } Here is the usage example to fetch values from a nested object: var result= json.JsonToDictionary()["outerField"] .JsonToDictionary()["innerField"]; Note that this solution does not cover the json objects starting as an array like [12, 13]. These objects can be read as an array in the begining and then the extension method can be applied on each item, in case the items are complex objects with their own properties.
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>"); } }