在反序列化大JSON对象JsonMaxLength例外 [英] JsonMaxLength exception on deserializing large json objects

查看:1050
本文介绍了在反序列化大JSON对象JsonMaxLength例外的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介:

Web应用程序,ASP.NET MVC 3,接受POCO模型类的一个实例(潜在的)大视场的控制器操作。

模型类:

 公共类查看
{
    [需要]
    [RegularEx pression(...)
    公共对象名称{搞定;组; }
    公共对象的详细信息{搞定;组; }
    公开对象的内容{搞定;组; } //问题字段
}

控制器动作:

  [ActionName(...)
[授权(...)]
[HttpPost]
公众的ActionResult CreateView的(查看视图)
{
    如果(ModelState.IsValid!){返回/ *一些这里的ActionResult * /;}
    ... //做其他的东西,创建数据库等对象返回有效结果
}

问题:

这是动作要能够接受较大的JSON对象(至少到百兆字节在一个单一的请求,这是没有笑话)。默认情况下我会见了一些限制,如的httpRuntime的maxRequestLength 等等 - 所有的解决除了MaxJsonLengh - 即默认为ValueProviderFactory JSON是不是能够处理这些对象

试过:

设置

 < System.Web.Extensions程序>
    <脚本>
      <&Web服务GT;
        < jsonSerialization maxJsonLength =2147483647/>
      < / WebServices的>
    < /脚本>
  < /system.web.extensions>


  • 于事无补。

创建自己的自定义ValueProviderFactory中所述@达林的答案在这里:

<一个href=\"http://stackoverflow.com/questions/9509721/jsonvalueproviderfactory-throws-request-too-large\">JsonValueProviderFactory抛出&QUOT;要求太大&QUOT;


  • 也失败了,因为我没有可能使用JSON.Net(由于非技术原因)。我想在这里实现反序列化正确的自己,但显然这是我的知识(还)有点以上。我能到我的JSON字符串反序列化为词典&LT;弦乐,对象&gt; 在这里,但是这不是我想要的 - 我想将其反序列化到我的可爱的POCO对象并用它们作为动作输入参数。

因此,问题:


  1. 任何人都知道更好的办法来克服这个问题,而实施普遍的风俗ValueProviderFactory?

  2. 有没有指定哪些具体的控制器和动作我想用我的自定义ValueProviderFactory可能性?如果我知道事先的行动比我就能JSON序列化到POCO无ValueProviderFactory ...
  3. 编写很多代码
  4. 我也想实施的特定问题的自定义ActionFilter,但我认为这是一个有点难看。

任何人都可以提出一个很好的解决方案?


解决方案

内置JsonValueProviderFactory忽略&LT; jsonSerialization maxJsonLength =5000/&GT; 设置。所以,你可以通过使用内置的实现编写自定义工厂:

 公共密封类MyJsonValueProviderFactory:ValueProviderFactory
{
    私有静态无效AddToBackingStore(词典&LT;字符串对象&gt;备份存储,字符串preFIX,对象的值)
    {
        IDictionary的&LT;字符串对象&gt; D =值的IDictionary&LT;字符串对象&gt ;;
        如果(D!= NULL)
        {
            的foreach(KeyValuePair&下;串,对象&gt;在D项)
            {
                AddToBackingStore(备份存储,MakePropertyKey(preFIX,entry.Key),entry.Value);
            }
            返回;
        }        IList的L =值作为IList的;
        如果(L!= NULL)
        {
            的for(int i = 0; I&LT; l.Count;我++)
            {
                AddToBackingStore(备份存储,MakeArrayKey(preFIX,I),L [我]);
            }
            返回;
        }        //原始
        备份存储[preFIX] =价值;
    }    私有静态对象GetDeserializedObject(ControllerContext controllerContext)
    {
        如果(!controllerContext.HttpContext.Request.ContentType.StartsWith(应用/ JSON,StringComparison.OrdinalIgnoreCase))
        {
            //不是JSON请求
            返回null;
        }        StreamReader的读者=新的StreamReader(controllerContext.HttpContext.Request.InputStream);
        字符串bodyText的= reader.ReadToEnd();
        如果(String.IsNullOrEmpty(bodyText的))
        {
            //没有JSON数据
            返回null;
        }        串行的JavaScriptSerializer =新的JavaScriptSerializer();
        serializer.MaxJsonLength = 2147483647;
        反对jsonData = serializer.DeserializeObject(bodyText的);
        返回jsonData;
    }    公众覆盖IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        如果(controllerContext == NULL)
        {
            抛出新的ArgumentNullException(controllerContext);
        }        反对jsonData = GetDeserializedObject(controllerContext);
        如果(jsonData == NULL)
        {
            返回null;
        }        字典&LT;字符串对象&gt;备份存储=新词典&LT;字符串对象&gt;(StringComparer.OrdinalIgnoreCase);
        AddToBackingStore(备份存储,的String.Empty,jsonData);
        返回新DictionaryValueProvider&LT;对象&gt;(备份存储,CultureInfo.CurrentCulture);
    }    私人静态字符串MakeArrayKey(字符串preFIX,INT指数)
    {
        返回preFIX +[+ index.ToString(CultureInfo.InvariantCulture)+];
    }    私人静态字符串MakePropertyKey(字符串preFIX,弦乐propertyName的)
    {
        回报(String.IsNullOrEmpty(preFIX))?参数propertyName:preFIX +。 + propertyName的;
    }
}

的唯一修改我相比,出厂默认是添加以下行所做的:

  serializer.MaxJsonLength = 2147483647;

不幸的是这家工厂是不可扩展可言,密封的东西,所以我不得不重新创建它。

和在的Application_Start

<$p$p><$c$c>ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(新MyJsonValueProviderFactory());

Intro:

Web application, ASP.NET MVC 3, a controller action that accepts an instance of POCO model class with (potentially) large field.

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 maxRequestLength etc. - all solved except MaxJsonLengh - meaning that default ValueProviderFactory for JSON is not capable of handling such objects.

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:

JsonValueProviderFactory throws "request too large"

  • 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.

So, the questions:

  1. Anyone knows better way to overcome the problem without implementing universal custom ValueProviderFactory?
  2. 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...
  3. I'm also thinking about implementing a custom ActionFilter for that specific problem, but I think it's a bit ugly.

Anyone can suggest a good solution?

解决方案

The built-in JsonValueProviderFactory ignores the <jsonSerialization maxJsonLength="50000000"/> setting. So you could write a custom factory by using the built-in implementation:

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:

ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());

这篇关于在反序列化大JSON对象JsonMaxLength例外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆