C# 反序列化大型 json 对象的 JsonMaxLength 异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9943727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JsonMaxLength exception on deserializing large json objects
提问by Sergey Kudriavtsev
Intro:
介绍:
Web application, ASP.NET MVC 3, a controller action that accepts an instance of POCO model class with (potentially) large field.
Web 应用程序,ASP.NET MVC 3,一个控制器操作,它接受具有(潜在)大字段的 POCO 模型类的实例。
Model class:
模型类:
public class View
{
[Required]
[RegularExpression(...)]
public object name { get; set; }
public object details { get; set; }
public object content { get; set; } // the problem field
}
Controller action:
控制器动作:
[ActionName(...)]
[Authorize(...)]
[HttpPost]
public ActionResult CreateView(View view)
{
if (!ModelState.IsValid) { return /*some ActionResult here*/;}
... //do other stuff, create object in db etc. return valid result
}
Problem:
问题:
An action should be able to accept large JSON objects (at least up to hundred megabytes in a single request and that's no joke). By default I met with several restrictions like httpRuntime maxRequestLengthetc. - all solved except MaxJsonLengh - meaning that default ValueProviderFactory for JSON is not capable of handling such objects.
一个动作应该能够接受大型 JSON 对象(在单个请求中至少高达 100 兆字节,这不是开玩笑)。默认情况下,我遇到了一些限制,例如httpRuntime maxRequestLength等 - 除 MaxJsonLengh 外都已解决 - 这意味着 JSON 的默认 ValueProviderFactory 无法处理此类对象。
Tried:
尝试:
Setting
环境
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647"/>
</webServices>
</scripting>
</system.web.extensions>
- does not help.
- 没有帮助。
Creating my own custom ValueProviderFactory as described in @Darin's answer here:
按照@Darin 的回答中所述创建我自己的自定义 ValueProviderFactory:
JsonValueProviderFactory throws "request too large"
JsonValueProviderFactory 抛出“请求太大”
- also failed because I have no possibility to use JSON.Net (due to non-technical reasons). I tried to implement correct deserialization here myself but apparently it's a bit above my knowledge (yet). I was able to deserialize my JSON string to
Dictionary<String,Object>here, but that's not what I want - I want to deserialize it to my lovely POCO objects and use them as input parameters for actions.
- 也失败了,因为我无法使用 JSON.Net(由于非技术原因)。我试图自己在这里实现正确的反序列化,但显然它有点超出我的知识(还)。我能够将我的 JSON 字符串反序列化到
Dictionary<String,Object>这里,但这不是我想要的 - 我想将它反序列化为我可爱的 POCO 对象并将它们用作操作的输入参数。
So, the questions:
所以,问题:
- Anyone knows better way to overcome the problem without implementing universal custom ValueProviderFactory?
- Is there a possibility to specify for what specific controller and action I want to use my custom ValueProviderFactory? If I know the action beforehand than I will be able to deserialize JSON to POCO without much coding in ValueProviderFactory...
- I'm also thinking about implementing a custom ActionFilter for that specific problem, but I think it's a bit ugly.
- 任何人都知道在不实施通用自定义 ValueProviderFactory 的情况下克服问题的更好方法吗?
- 是否可以指定我要使用自定义 ValueProviderFactory 的特定控制器和操作?如果我事先知道该操作,那么我将能够将 JSON 反序列化为 POCO,而无需在 ValueProviderFactory 中进行大量编码...
- 我也在考虑为那个特定问题实现一个自定义的 ActionFilter,但我认为它有点难看。
Anyone can suggest a good solution?
任何人都可以提出一个好的解决方案?
采纳答案by Darin Dimitrov
The built-in JsonValueProviderFactory ignores the <jsonSerialization maxJsonLength="50000000"/>setting. So you could write a custom factory by using the built-in implementation:
内置的 JsonValueProviderFactory 会忽略该<jsonSerialization maxJsonLength="50000000"/>设置。因此,您可以使用内置实现编写自定义工厂:
public sealed class MyJsonValueProviderFactory : 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 = 2147483647;
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;
}
}
The only modification I did compared to the default factory is adding the following line:
与默认工厂相比,我所做的唯一修改是添加以下行:
serializer.MaxJsonLength = 2147483647;
Unfortunately this factory is not extensible at all, sealed stuff so I had to recreate it.
不幸的是,这个工厂根本不可扩展,密封的东西所以我不得不重新创建它。
and in your Application_Start:
并在您的Application_Start:
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());
回答by Oliver
I found that the maxRequestLength did not solve the problem however. I resolved my issue with the below setting. It is cleaner than having to implement a custom ValueProviderFactory
但是我发现 maxRequestLength 并没有解决问题。我通过以下设置解决了我的问题。它比必须实现自定义ValueProviderFactory更干净
<appSettings>
<add key="aspnet:MaxJsonDeserializerMembers" value="150000" />
</appSettings>
Credit goes to the following questions:
归功于以下问题:
JsonValueProviderFactory throws "request too large"
JsonValueProviderFactory 抛出“请求太大”
Getting "The JSON request was too large to be deserialized"
This setting obviously relates to a highly complex json model and not the actual size.
此设置显然与高度复杂的 json 模型有关,而不是实际大小。
回答by Gonzalo Rubio Torrente
The solution of Darin Dimitrov works for me but i need reset the position of the stream of the request before read it, adding this line:
Darin Dimitrov 的解决方案对我有用,但我需要在读取之前重置请求流的位置,添加以下行:
controllerContext.HttpContext.Request.InputStream.Position = 0;
controllerContext.HttpContext.Request.InputStream.Position = 0;
So now, the method GetDeserializedObject looks like this:
所以现在,方法 GetDeserializedObject 看起来像这样:
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
controllerContext.HttpContext.Request.InputStream.Position = 0;
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 = 2147483647;
object jsonData = serializer.DeserializeObject(bodyText);
return jsonData;
}

