Web API 2: cómo devolver JSON con nombres de propiedad camelCased, sobre objetos y sus subobjetos

ACTUALIZAR

Gracias por todas las respuestas. Estoy en un nuevo proyecto y parece que finalmente llegué al fondo de esto: parece que el siguiente código fue el culpable:

public static HttpResponseMessage GetHttpSuccessResponse(object response, HttpStatusCode code = HttpStatusCode.OK) { return new HttpResponseMessage() { StatusCode = code, Content = response != null ? new JsonContent(response) : null }; } 

en otra parte…

 public JsonContent(object obj) { var encoded = JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore } ); _value = JObject.Parse(encoded); Headers.ContentType = new MediaTypeHeaderValue("application/json"); } 

Había pasado por alto el inofensivo aspecto de JsonContent asumiendo que era WebAPI pero no.

Esto se usa en todas partes … ¿Puedo ser el primero en decir, wtf? O tal vez debería ser “¿Por qué están haciendo esto?”


la pregunta original sigue

Uno hubiera pensado que esto sería una configuración de configuración simple, pero ahora me ha eludido por mucho tiempo.

He visto varias soluciones y respuestas:

https://gist.github.com/rdingwall/2012642

no parece aplicarse a la última versión de WebAPI …

Lo siguiente no parece funcionar: los nombres de propiedad todavía están PascalCased.

 var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.UseDataContractJsonSerializer = true; json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

La respuesta de Mayank aquí: los Sub-Objetos de CamelCase JSON WebAPI (Objetos nesteds, objetos secundarios) parecían una respuesta insatisfactoria pero viable hasta que me di cuenta de que estos atributos deberían agregarse al código generado ya que estamos usando linq2sql …

¿Alguna forma de hacer esto automáticamente? Este ‘desagradable’ me ha plagado por un largo tiempo ahora.

Poniendo todo junto, obtienes …

 protected void Application_Start() { HttpConfiguration config = GlobalConfiguration.Configuration; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; } 

Esto es lo que funcionó para mí:

 internal static class ViewHelpers { public static JsonSerializerSettings CamelCase { get { return new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; } } } 

Y entonces:

 [HttpGet] [Route("api/campaign/list")] public IHttpActionResult ListExistingCampaigns() { var domainResults = _campaignService.ListExistingCampaigns(); return Json(domainResults, ViewHelpers.CamelCase); } 

La clase CamelCasePropertyNamesContractResolver proviene de Newtonsoft.Json.dll en la biblioteca Json.NET .

Todas las respuestas anteriores no funcionaron para mí con Owin Hosting y Ninject. Esto es lo que funcionó para mí:

 public partial class Startup { public void Configuration(IAppBuilder app) { // Get the ninject kernel from our IoC. var kernel = IoC.GetKernel(); var config = new HttpConfiguration(); // More config settings and OWIN middleware goes here. // Configure camel case json results. ConfigureCamelCase(config); // Use ninject middleware. app.UseNinjectMiddleware(() => kernel); // Use ninject web api. app.UseNinjectWebApi(config); } ///  /// Configure all JSON responses to have camel case property names. ///  private void ConfigureCamelCase(HttpConfiguration config) { var jsonFormatter = config.Formatters.JsonFormatter; // This next line is not required for it to work, but here for completeness - ignore data contracts. jsonFormatter.UseDataContractJsonSerializer = false; var settings = jsonFormatter.SerializerSettings; #if DEBUG // Pretty json for developers. settings.Formatting = Formatting.Indented; #else settings.Formatting = Formatting.None; #endif settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } 

La diferencia clave es: nueva HttpConfiguration () en lugar de GlobalConfiguration.Configuration.

Resulta que

 return Json(result); 

fue el culpable, causando que el proceso de serialización ignore la configuración de camelcase. Y eso

 return Request.CreateResponse(HttpStatusCode.OK, result, Request.GetConfiguration()); 

era el droide que estaba buscando.

también

 json.UseDataContractJsonSerializer = true; 

Estaba poniendo una llave inglesa en las obras y resultó que NO era el droide que estaba buscando.

Código de WebApiConfig:

  public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); //This line sets json serializer's ContractResolver to CamelCasePropertyNamesContractResolver, // so API will return json using camel case config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } 

Asegúrese de que su método de acción API devuelva datos de la siguiente manera y que haya instalado la última versión de Json.Net/Newtonsoft.Json instalada:

  [HttpGet] public HttpResponseMessage List() { try { var result = /*write code to fetch your result*/; return Request.CreateResponse(HttpStatusCode.OK, cruises); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } 

En su Inicio de Owin, agregue esta línea …

  public class Startup { public void Configuration(IAppBuilder app) { var webApiConfiguration = ConfigureWebApi(); app.UseWebApi(webApiConfiguration); } private HttpConfiguration ConfigureWebApi() { var config = new HttpConfiguration(); // ADD THIS LINE HERE AND DONE config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.MapHttpAttributeRoutes(); return config; } } 

Aquí hay algo oscuro, cuando el atributo de ruta no coincidía con la URL GET pero la url GET coincidía con el nombre del método, la directiva jsonserializer camel case se ignoraba, por ejemplo

http: // sitio web / api / geo / geodata

 //uppercase fail cakes [HttpGet] [Route("countries")] public async Task GeoData() { return await geoService.GetGeoData(); } //lowercase nomnomnom cakes [HttpGet] [Route("geodata")] public async Task GeoData() { return await geoService.GetGeoData(); } 

Lo he solucionado de las siguientes maneras.

 [AllowAnonymous] [HttpGet()] public HttpResponseMessage GetAllItems(int moduleId) { HttpConfiguration config = new HttpConfiguration(); config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; try { List itemList = GetItemsFromDatabase(moduleId); return Request.CreateResponse(HttpStatusCode.OK, itemList, config); } catch (System.Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message); } } 

Estoy usando WebApi con Breeze y ejecuté el mismo problema cuando bash ejecutar una acción que no es fácil en un controlador de brisa. Intenté usar la aplicación Request.GetConfiguration pero el mismo resultado. Por lo tanto, cuando accedo al objeto devuelto por Request.GetConfiguration me doy cuenta de que el serializador utilizado por solicitud es el que usa el servidor Breeze para hacer su magia. De cualquier forma, resolví mi problema creando una HttpConfiguration diferente:

 public static HttpConfiguration BreezeControllerCamelCase { get { var config = new HttpConfiguration(); var jsonSerializerSettings = config.Formatters.JsonFormatter.SerializerSettings; jsonSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); jsonSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false; return config; } } 

y pasarlo como parámetro en Request.CreateResponse de la siguiente manera:

 return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);