¿Cómo puedo devolver camelCase JSON serializado por JSON.NET desde los métodos del controlador ASP.NET MVC?

246

Mi problema es que deseo devolver datos JSON camelCased (a diferencia del PascalCase estándar) a través de ActionResult s de los métodos del controlador ASP.NET MVC, serializados por JSON.NET .

Como ejemplo, considere la siguiente clase de C #:

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

De forma predeterminada, al devolver una instancia de esta clase desde un controlador MVC como JSON, se serializará de la siguiente manera:

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

Me gustaría que se serialice (por JSON.NET) como:

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

¿Cómo hago esto?

aknuds1
fuente

Respuestas:

389

o simplemente pon:

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

Por ejemplo:

return new ContentResult
{
    ContentType = "application/json",
    Content = JsonConvert.SerializeObject(new { content = result, rows = dto }, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }),
    ContentEncoding = Encoding.UTF8
};
WebDever
fuente
2
Sin embargo, es más complejo de usar, ya que debe configurar un ContentResult para cada método de controlador.
aknuds1
2
Sí, entiendo que su respuesta fue una solución reutilizable, mi punto es dejar más claro que solo es un parámetro en el método Serialize.
WebDever
1
Si está devolviendo JSON de un Controllermétodo, probablemente debería usar un ApiController, en cuyo caso esta respuesta funciona muy bien.
Simon Hartcher
1
@SimonHartcher Tenga en cuenta el alcance de la pregunta, no el caso general.
aknuds1
1
El tipo de contenido válido para JSON es application/json, no text/plain.
Fred
94

Encontré una excelente solución a este problema en el blog de Mats Karlsson . La solución es escribir una subclase de ActionResult que serialice los datos a través de JSON.NET, configurando este último para que siga la convención 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));
    }
}

Luego use esta clase de la siguiente manera en su método de controlador MVC:

public ActionResult GetPerson()
{
    return new JsonCamelCaseResult(new Person { FirstName = "Joe", LastName = "Public" }, JsonRequestBehavior.AllowGet)};
}
aknuds1
fuente
3
Respuesta perfecta: ¡limpia y reutilizable! Gracias.
lijadora
1
Mientras esta solución aún funciona. pero se sugirió hace 4 años. ¿Tenemos una mejor solución?
SharpCoder
59

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

Básicamente, agregue este código a su Application_Start:

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Assaf S.
fuente
44
Web API y MVC se han fusionado en ASP.NET 6
AlexFoxGill
1
Vinculación por conveniencia; esta configuración funciona muy bien con esta respuesta: stackoverflow.com/a/26068063/398630 (pregunta diferente, pero las uso juntas, y este enlace podría ahorrarme a mí y a otros googlear en el futuro).
BrainSlugs83
37

Creo que esta es la respuesta simple que estás buscando. Es del blog de Shawn Wildermuth :

// Add MVC services to the services container.
services.AddMvc()
  .AddJsonOptions(opts =>
  {
    opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  });
Quantium
fuente
2
Mis disculpas, muchachos. Leí esta publicación demasiado rápido. Es para ASP.NET 5.
Quantium
8
Irónicamente, vine aquí buscando una respuesta a la pregunta que respondiste aquí, así que aunque no era la respuesta a la pregunta del OP, me ayudó de todos modos. ¡Gracias! :)
porcus
1
¡Segundo lo que dijo @porcus! Gracias @Quantium!
Gromer
44
Para su ASP.NET Core 1.0, el caso es camel por defecto OOTB
Chris Marisic
3
Resulta que este no es (exactamente) el predeterminado para .NET Core 1.0 después de todo. Esta solución afecta las propiedades dinámicas y esas no se ven afectadas de manera predeterminada. stackoverflow.com/questions/41329279/…
Niels Brinch
13

Una alternativa al filtro personalizado es crear un método de extensión para serializar cualquier objeto a 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);
    }
}

Luego llámelo cuando regrese de la acción del controlador.

return Content(person.ToJson(), "application/json");
Stuart Hallows
fuente
Elegante y simple
markau
1
Incluso podría transferir la configuración a un campo de solo lectura estático y agregar un método de complemento FromJson.
Vapor en el callejón
8

Más simple es mejor IMO!

¿Por qué no haces esto?

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

El controlador de clase base simple

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
fuente
7

En ASP.NET Core MVC.

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

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

        return Json(data, settings);
    }
Fred
fuente
Y aún mejor, póngalo en el archivo Startup.cs.
FatAlbert
6

A continuación se muestra un método de acción que devuelve una cadena json (cameCase) al serializar una 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);
    }

Tenga en cuenta que la instancia de JsonSerializerSettings pasó como segundo parámetro. Eso es lo que hace que el camelCase suceda.

DanKodi
fuente
4

Me gustó esto:

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 es un método de extensión simple en el núcleo MVC, le dará la capacidad ToJson () a cada objeto en su proyecto, en mi opinión, en un proyecto MVC la mayoría de los objetos deberían tener la capacidad de convertirse en json, por supuesto, depende :)

Ali Alp
fuente
Considere extraer la variable "configuración" fuera del método (como campo estático privado "camelCaseSettings") para que no inicialice una nueva variable cada vez que se llama al método ToJson.
Ekus
4

Debe establecer la configuración en el archivo 'Startup.cs'

También debe definirlo en los valores predeterminados de JsonConvert, esto es si luego desea usar directamente la biblioteca para serializar un 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
fuente
Tenga en cuenta que esta respuesta es correcta para ASP.NET Core, pero no para ASP.NET (que es el marco en la pregunta).
Nate Barbettini
0

Si está devolviendo ActionResult en .net core web api, o el resultado de IHttpAction, entonces puede envolver su modelo en un método Ok () que coincidirá con el caso en su front-end y serializarlo para usted. No es necesario usar JsonConvert. :)

LukePerrin
fuente