Como posso retornar o camelCase JSON serializado pelo JSON.NET a partir dos métodos do controlador ASP.NET MVC?

246

Meu problema é que eu desejo retornar dados JSON camelCased (ao contrário do PascalCase padrão) via ActionResult s dos métodos do controlador ASP.NET MVC, serializados pelo JSON.NET .

Como exemplo, considere a seguinte classe C #:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Por padrão, ao retornar uma instância desta classe de um controlador MVC como JSON, ela será serializada da seguinte maneira:

{
  "FirstName": "Joe",
  "LastName": "Public"
}

Gostaria que fosse serializado (por JSON.NET) como:

{
  "firstName": "Joe",
  "lastName": "Public"
}

Como eu faço isso?

aknuds1
fonte

Respostas:

389

ou simplesmente:

JsonConvert.SerializeObject(
    <YOUR OBJECT>, 
    new JsonSerializerSettings 
    { 
        ContractResolver = new CamelCasePropertyNamesContractResolver() 
    });

Por exemplo:

return new ContentResult
{
    ContentType = "application/json",
    Content = JsonConvert.SerializeObject(new { content = result, rows = dto }, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }),
    ContentEncoding = Encoding.UTF8
};
WebDever
fonte
2
Porém, isso é mais complexo de usar, pois você deve configurar um ContentResult para cada método do controlador.
aknuds1
2
Sim, eu entendo que sua resposta foi uma solução reutilizável, meu objetivo é deixar mais claro que é apenas um parâmetro no método Serialize.
WebDever 18/03
1
Se você está retornando JSON de um Controllermétodo, provavelmente deve usar um ApiController, nesse caso, essa resposta funciona muito bem.
Simon Hartcher
1
@SimonHartcher Considere o escopo da questão, não o caso geral.
aknuds1
1
O tipo de conteúdo válido para JSON é application/json, não text/plain.
Fred
94

Encontrei uma excelente solução para esse problema no blog de Mats Karlsson . A solução é escrever uma subclasse de ActionResult que serialize dados via JSON.NET, configurando o último para seguir a convenção camelCase:

public class JsonCamelCaseResult : ActionResult
{
    public JsonCamelCaseResult(object data, JsonRequestBehavior jsonRequestBehavior)
    {
        Data = data;
        JsonRequestBehavior = jsonRequestBehavior;
    }

    public Encoding ContentEncoding { get; set; }

    public string ContentType { get; set; }

    public object Data { get; set; }

    public JsonRequestBehavior JsonRequestBehavior { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.");
        }

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data == null)
            return;

        var jsonSerializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
        response.Write(JsonConvert.SerializeObject(Data, jsonSerializerSettings));
    }
}

Em seguida, use esta classe da seguinte maneira no seu método de controlador MVC:

public ActionResult GetPerson()
{
    return new JsonCamelCaseResult(new Person { FirstName = "Joe", LastName = "Public" }, JsonRequestBehavior.AllowGet)};
}
aknuds1
fonte
3
Resposta perfeita: limpo e reutilizável! Obrigado.
lixadeira
1
Enquanto esta solução ainda estiver funcionando. mas foi sugerido 4 anos atrás. Temos uma solução melhor?
SharpCoder
59

Para WebAPI , confira este link: http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx

Basicamente, adicione este código ao seu Application_Start:

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Assaf S.
fonte
4
A API da Web e o MVC foram mesclados no ASP.NET 6
AlexFoxGill
1
Vinculação por conveniência; essa configuração funciona muito bem com esta resposta: stackoverflow.com/a/26068063/398630 (pergunta diferente, mas eu as uso juntas, e esse link pode poupar a mim e a outras pessoas pesquisando no futuro).
BrainSlugs83
37

Eu acho que esta é a resposta simples que você está procurando. É do blog de Shawn Wildermuth :

// Add MVC services to the services container.
services.AddMvc()
  .AddJsonOptions(opts =>
  {
    opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  });
Quantium
fonte
2
Minhas desculpas, pessoal. Eu li esta postagem muito rapidamente. É para o ASP.NET 5.
Quantium
8
Ironicamente, eu vim aqui procurando uma resposta para a pergunta que você respondeu aqui; portanto, embora não fosse a resposta para a pergunta do OP, isso me ajudou de qualquer maneira. Obrigado! :)
porcus 14/03
1
Eu segundo o que @porcus disse! Obrigado @Quantium!
Gromer
4
FYI para ASP.NET Núcleo 1,0-lo do caso camelo por OOTB padrão
Chris Marisic
3
Acontece que este não é (exatamente) o padrão para o .NET Core 1.0, afinal. Esta solução afeta propriedades dinâmicas e essas não são afetadas por padrão. stackoverflow.com/questions/41329279/…
Niels Brinch
13

Uma alternativa ao filtro customizado é criar um método de extensão para serializar qualquer objeto para JSON.

public static class ObjectExtensions
{
    /// <summary>Serializes the object to a JSON string.</summary>
    /// <returns>A JSON string representation of the object.</returns>
    public static string ToJson(this object value)
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Converters = new List<JsonConverter> { new StringEnumConverter() }
        };

        return JsonConvert.SerializeObject(value, settings);
    }
}

Em seguida, chame-o ao retornar da ação do controlador.

return Content(person.ToJson(), "application/json");
Stuart Hallows
fonte
Elegante e simples.
Markau
1
Você pode até transferir as configurações para um campo estático somente leitura e adicionar um método de complemento FromJson.
Vapor no Beco
8

Mais simples é melhor IMO!

Por que você não faz isso?

public class CourseController : JsonController
{
    public ActionResult ManageCoursesModel()
    {
        return JsonContent(<somedata>);
    }
}

O controlador simples da classe base

public class JsonController : BaseController
{
    protected ContentResult JsonContent(Object data)
    {
        return new ContentResult
        {
            ContentType = "application/json",
             Content = JsonConvert.SerializeObject(data, new JsonSerializerSettings { 
              ContractResolver = new CamelCasePropertyNamesContractResolver() }),
            ContentEncoding = Encoding.UTF8
        };
    }
}
jwize
fonte
7

No ASP.NET Core MVC.

    public IActionResult Foo()
    {
        var data = GetData();

        var settings = new JsonSerializerSettings 
        { 
            ContractResolver = new CamelCasePropertyNamesContractResolver() 
        });

        return Json(data, settings);
    }
Fred
fonte
E melhor ainda, coloque-o no arquivo Startup.cs.
FatAlbert
6

Abaixo está um método de ação que retorna uma string json (cameCase) serializando uma matriz de objetos.

public string GetSerializedCourseVms()
    {
        var courses = new[]
        {
            new CourseVm{Number = "CREA101", Name = "Care of Magical Creatures", Instructor ="Rubeus Hagrid"},
            new CourseVm{Number = "DARK502", Name = "Defence against dark arts", Instructor ="Severus Snape"},
            new CourseVm{Number = "TRAN201", Name = "Transfiguration", Instructor ="Minerva McGonal"}
        };
        var camelCaseFormatter = new JsonSerializerSettings();
        camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
        return JsonConvert.SerializeObject(courses, camelCaseFormatter);
    }

Observe que a instância JsonSerializerSettings passou como o segundo parâmetro. É isso que faz o camelCase acontecer.

DanKodi
fonte
4

Eu fiz assim:

public static class JsonExtension
{
    public static string ToJson(this object value)
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Serialize
        };
        return JsonConvert.SerializeObject(value, settings);
    }
}

este é um método simples de extensão no núcleo do MVC, que dará a capacidade do ToJson () para todos os objetos do seu projeto. Na minha opinião, em um projeto do MVC, a maioria dos objetos deve ter a capacidade de se tornar json, claro que depende :)

Ali Alp
fonte
Considere extrair a variável "settings" fora do método (como campo estático privado "camelCaseSettings") para não inicializar uma nova variável toda vez que o método ToJson for chamado.
Ekus 27/02
4

Você deve definir as configurações no arquivo 'Startup.cs'

Você também deve defini-lo nos valores padrão do JsonConvert, caso deseje posteriormente usar diretamente a biblioteca para serializar um objeto.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(options => {
                options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
    }
Daniel Sánchez
fonte
Observe que esta resposta está correta para o ASP.NET Core, mas não para o ASP.NET (que é a estrutura da pergunta).
Nate Barbettini 15/10/19
0

Se você estiver retornando o ActionResult no resultado da api da web .net core ou IHttpAction, basta encerrar o modelo em um método Ok () que corresponda ao caso no seu front end e o serialize para você. Não há necessidade de usar o JsonConvert. :)

LukePerrin
fonte