Forçar nomes de propriedades em letras minúsculas de Json () na ASP.NET MVC

89

Dada a seguinte aula,

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

    public string Message { get; set; }
}

Estou retornando um desses em uma ação de controlador assim,

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

No entanto, minha estrutura do lado do cliente espera que essas propriedades sejam sucesso e mensagem em letras minúsculas. Sem realmente ter que ter nomes de propriedades em letras minúsculas, essa é uma maneira de realizar esse pensamento, a chamada de função Json normal?

James Hughes
fonte

Respostas:

130

A maneira de conseguir isso é implementar um custom JsonResultcomo este: Criando um ValueType customizado e Serialising com um JsonResult customizado (link original morto) .

E use um serializador alternativo, como JSON.NET , que suporta esse tipo de comportamento, por exemplo:

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() 
    }
);

Resulta em

{
  "name": "Widget",
  "expiryDate": "\/Date(1292868060000)\/",
  "price": 9.99,
  "sizes": [
    "Small",
    "Medium",
    "Large"
  ]
}
James Hughes
fonte
1
Este link funciona: james.newtonking.com/json/help/index.html?topic=html/...
Josef Engelfrost
Se você está usando JSON.NET e não quer camelCase, mas snake_case, verifique esta essência, realmente me ajudou! gist.github.com/crallen/9238178
Niclas Lindqvist
Como faço para desserializar? Ex. de "pequeno" para "Pequeno"
torre
1
@NiclasLindqvist Para versões JSON.NET modernas, há uma maneira muito mais simples de obter snake_case: newtonsoft.com/json/help/html/NamingStrategySnakeCase.htm
Søren Boisen
16

Alterar o serializador é simples se você estiver usando a API Web, mas infelizmente o próprio MVC usa JavaScriptSerializersem opção para alterar isso para usar JSON.Net.

A resposta de James e a resposta de Daniel fornecem a flexibilidade de JSON.Net, mas significa que em todos os lugares onde você normalmente faria, return Json(obj)você terá que mudar para return new JsonNetResult(obj)ou semelhante, o que se você tiver um grande projeto pode ser um problema, e também não é muito flexível se você mudar de ideia sobre o serializador que deseja usar.


Decidi seguir o ActionFiltercaminho. O código a seguir permite que você execute qualquer ação usando JsonResulte simplesmente aplique um atributo a ele para usar JSON.Net (com propriedades em minúsculas):

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

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

Você pode até mesmo configurar isso para aplicar-se automaticamente a todas as ações (apenas com o menor impacto de desempenho de uma isverificação):

FilterConfig.cs

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

O 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
fonte
10

Com a minha solução, você pode renomear cada propriedade que desejar.

Encontrei parte da solução aqui e no 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 no meu controlador, eu possa fazer isso

        return new JsonNetResult(result);

No meu modelo, agora posso ter:

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

Observe que agora, você deve definir o JsonPropertyAttributepara todas as propriedades que deseja serializar.

Daniel
fonte
1

Embora seja uma questão antiga, espero que o snippet de código abaixo seja útil para outros,

Eu fiz abaixo com 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
fonte
0

Você pode adicionar essa configuração a Global.asaxe funcionará em qualquer lugar.

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
fonte