Forzar nombres de propiedad en minúsculas de Json () en ASP.NET MVC

89

Dada la siguiente clase,

public class Result
{      
    public bool Success { get; set; }

    public string Message { get; set; }
}

Estoy devolviendo uno de estos en una acción de controlador como ese,

return Json(new Result() { Success = true, Message = "test"})

Sin embargo, mi marco del lado del cliente espera que estas propiedades sean éxito y mensaje en minúsculas. Sin tener que tener nombres de propiedad en minúsculas, ¿es esa una forma de lograr este pensamiento, la llamada normal a la función Json?

James Hughes
fuente

Respuestas:

130

La forma de lograr esto es implementar un personalizado JsonResultcomo aquí: Crear un ValueType personalizado y serializar con un JsonResult personalizado (enlace original muerto) .

Y use un serializador alternativo como JSON.NET , que admite este tipo de comportamiento, por ejemplo:

Product product = new Product
{
  ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc),
  Name = "Widget",
  Price = 9.99m,
  Sizes = new[] {"Small", "Medium", "Large"}
};

string json = 
  JsonConvert.SerializeObject(
    product,
    Formatting.Indented,
    new JsonSerializerSettings 
    { 
      ContractResolver = new CamelCasePropertyNamesContractResolver() 
    }
);

Resultados en

{
  "name": "Widget",
  "expiryDate": "\/Date(1292868060000)\/",
  "price": 9.99,
  "sizes": [
    "Small",
    "Medium",
    "Large"
  ]
}
James Hughes
fuente
1
Este enlace funciona: james.newtonking.com/json/help/index.html?topic=html/…
Josef Engelfrost
Si está utilizando JSON.NET y no quiere camelCase sino snake_case, consulte esta esencia, ¡realmente me ayudó! gist.github.com/crallen/9238178
Niclas Lindqvist
¿Cómo deserializo? Ex. de "pequeño" a "pequeño"
torre
1
@NiclasLindqvist Para las versiones modernas de JSON.NET, hay una forma mucho más sencilla de obtener snake_case: newtonsoft.com/json/help/html/NamingStrategySnakeCase.htm
Søren Boisen
17

Cambiar el serializador es simple si está usando la API web, pero desafortunadamente MVC sí lo usa JavaScriptSerializersin opción para cambiar esto para usar JSON.Net.

La respuesta de James y la respuesta de Daniel le brindan la flexibilidad de JSON.Net, pero significa que en todos los lugares donde normalmente lo haría return Json(obj), debe cambiar a return new JsonNetResult(obj)o similar, lo que si tiene un gran proyecto podría resultar un problema, y ​​tampoco es muy flexible si cambia de opinión sobre el serializador que desea utilizar.


Decidí seguir la ActionFilterruta. El siguiente código le permite realizar cualquier acción usando JsonResulty simplemente aplicarle un atributo para usar JSON.Net (con propiedades en minúsculas):

[JsonNetFilter]
[HttpPost]
public ActionResult SomeJson()
{
    return Json(new { Hello = "world" });
}

// outputs: { "hello": "world" }

Incluso puede configurar esto para que se aplique automáticamente a todas las acciones (con solo el menor impacto de rendimiento de una isverificación):

FilterConfig.cs

// ...
filters.Add(new JsonNetFilterAttribute());

El código

public class JsonNetFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is JsonResult == false)
            return;

        filterContext.Result = new CustomJsonResult((JsonResult)filterContext.Result);
    }

    private class CustomJsonResult : JsonResult
    {
        public CustomJsonResult(JsonResult jsonResult)
        {
            this.ContentEncoding = jsonResult.ContentEncoding;
            this.ContentType = jsonResult.ContentType;
            this.Data = jsonResult.Data;
            this.JsonRequestBehavior = jsonResult.JsonRequestBehavior;
            this.MaxJsonLength = jsonResult.MaxJsonLength;
            this.RecursionLimit = jsonResult.RecursionLimit;
        }

        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("GET not allowed! Change JsonRequestBehavior to AllowGet.");

            var 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)
            {
                var json = JsonConvert.SerializeObject(
                    this.Data,
                    new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        });

                response.Write(json);
            }
        }
    }
}
dav_i
fuente
10

Con mi solución, puede cambiar el nombre de cada propiedad que desee.

Encontré parte de la solución aquí y en SO

public class JsonNetResult : ActionResult
    {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }
        public object Data { get; set; }

        public JsonSerializerSettings SerializerSettings { get; set; }
        public Formatting Formatting { get; set; }

        public JsonNetResult(object data, Formatting formatting)
            : this(data)
        {
            Formatting = formatting;
        }

        public JsonNetResult(object data):this()
        {
            Data = data;
        }

        public JsonNetResult()
        {
            Formatting = Formatting.None;
            SerializerSettings = new JsonSerializerSettings();
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            var response = context.HttpContext.Response;
            response.ContentType = !string.IsNullOrEmpty(ContentType)
              ? ContentType
              : "application/json";
            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (Data == null) return;

            var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
            var serializer = JsonSerializer.Create(SerializerSettings);
            serializer.Serialize(writer, Data);
            writer.Flush();
        }
    }

Para que en mi controlador, pueda hacer eso

        return new JsonNetResult(result);

En mi modelo, ahora puedo tener:

    [JsonProperty(PropertyName = "n")]
    public string Name { get; set; }

Tenga en cuenta que ahora, debe configurar el JsonPropertyAttributepara cada propiedad que desee serializar.

Daniel
fuente
1

Aunque es una pregunta antigua, espero que el siguiente fragmento de código sea útil para otros.

Lo hice a continuación con MVC5 Web API.

public JsonResult<Response> Post(Request request)
    {
        var response = new Response();

        //YOUR LOGIC IN THE METHOD
        //.......
        //.......

        return Json<Response>(response, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() });
    }
pgcan
fuente
0

Puede agregar esta configuración a Global.asax, y funcionará en todas partes.

public class Global : HttpApplication
{   
    void Application_Start(object sender, EventArgs e)
    {
        //....
         JsonConvert.DefaultSettings = () =>
         {
             var settings = new JsonSerializerSettings
             {
                 ContractResolver = new CamelCasePropertyNamesContractResolver(),
                 PreserveReferencesHandling = PreserveReferencesHandling.None,
                 Formatting = Formatting.None
             };

             return settings;
         }; 
         //....
     }
}
Maksym Labutin
fuente