c# - que - Excepción de JsonMaxLength al deserializar objetos json grandes
serializar json c# (3)
El JsonValueProviderFactory incorporado ignora la <jsonSerialization maxJsonLength="50000000"/>
. Así que podrías escribir una fábrica personalizada usando la implementación incorporada:
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;
}
}
La única modificación que hice en comparación con la fábrica predeterminada es agregar la siguiente línea:
serializer.MaxJsonLength = 2147483647;
Desafortunadamente, esta fábrica no es extensible en absoluto, cosas selladas, así que tuve que recrearla.
y en tu Application_Start
:
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());
Introducción:
La aplicación web, ASP.NET MVC 3, una acción de controlador que acepta una instancia de clase de modelo POCO con (potencialmente) campo grande.
Clase de modelo
public class View
{
[Required]
[RegularExpression(...)]
public object name { get; set; }
public object details { get; set; }
public object content { get; set; } // the problem field
}
Acción del controlador:
[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
}
Problema:
Una acción debe poder aceptar objetos JSON grandes (al menos hasta cien megabytes en una sola solicitud y eso no es una broma). De forma predeterminada, me encontré con varias restricciones como httpRuntime maxRequestLength
etc., todas resueltas, excepto MaxJsonLengh, lo que significa que ValueProviderFactory predeterminada para JSON no es capaz de manejar tales objetos.
Intentó:
Ajuste
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647"/>
</webServices>
</scripting>
</system.web.extensions>
- no ayuda.
Creando mi propia ValueProviderFactory personalizada como se describe en la respuesta de @Darin aquí:
JsonValueProviderFactory lanza "solicitud demasiado grande"
- también fallí porque no tengo la posibilidad de usar JSON.Net (debido a razones no técnicas). Intenté implementar la deserialización correcta aquí, pero aparentemente está un poco por encima de mi conocimiento (todavía). Pude deserializar mi cadena JSON en
Dictionary<String,Object>
aquí, pero eso no es lo que quiero, quiero deserializarlo en mis adorables objetos POCO y usarlos como parámetros de entrada para las acciones.
Entonces, las preguntas:
- ¿Alguien sabe una mejor manera de superar el problema sin implementar la ValueProviderFactory personalizada universal?
- ¿Existe la posibilidad de especificar para qué controlador y acción específicos quiero usar mi ValueProviderFactory personalizado? Si conozco la acción de antemano, podré deserializar JSON a POCO sin mucha codificación en ValueProviderFactory ...
- También estoy pensando en implementar un ActionFilter personalizado para ese problema específico, pero creo que es un poco feo.
¿Alguien puede sugerir una buena solución?
Encontré que el maxRequestLength no solucionó el problema sin embargo. Resolví mi problema con la configuración de abajo. Es más limpio que tener que implementar un ValueProviderFactory personalizado
<appSettings>
<add key="aspnet:MaxJsonDeserializerMembers" value="150000" />
</appSettings>
El crédito va a las siguientes preguntas:
JsonValueProviderFactory lanza "solicitud demasiado grande"
Obteniendo "La solicitud JSON era demasiado grande para ser deserializada"
Esta configuración obviamente se relaciona con un modelo json altamente complejo y no con el tamaño real.
La solución de Darin Dimitrov funciona para mí, pero necesito restablecer la posición del flujo de la solicitud antes de leerla, agregando esta línea:
controllerContext.HttpContext.Request.InputStream.Position = 0;
Así que ahora, el método GetDeserializedObject se ve así:
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;
}