Web API 2: how to return JSON with camelCased property names, on objects and their sub-objects
C#.Netasp.net Web-Apijson.netasp.net Web-Api2C# Problem Overview
UPDATE
Thanks for all the answers. I am on a new project and it looks like I've finally got to the bottom of this: It looks like the following code was in fact to blame:
public static HttpResponseMessage GetHttpSuccessResponse(object response, HttpStatusCode code = HttpStatusCode.OK)
{
return new HttpResponseMessage()
{
StatusCode = code,
Content = response != null ? new JsonContent(response) : null
};
}
elsewhere...
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");
}
I had overlooked the innocuous looking JsonContent assuming it was WebAPI but no.
This is used everywhere... Can I just be the first to say, wtf? Or perhaps that should be "Why are they doing this?"
original question follows
One would have thought this would be a simple config setting, but it's eluded me for too long now.
I have looked at various solutions and answers:
https://gist.github.com/rdingwall/2012642
doesn't seem to apply to latest WebAPI version...
The following doesn't seem to work - property names are still PascalCased.
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = true;
json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Mayank's answer here: https://stackoverflow.com/questions/23370619/camelcase-json-webapi-sub-objects-nested-objects-child-objects seemed like an unsatisfactory but workable answer until I realised these attributes would have to be added to generated code as we are using linq2sql...
Any way to do this automatically? This 'nasty' has plagued me for a long time now.
C# Solutions
Solution 1 - C#
Putting it all together you get...
protected void Application_Start()
{
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
}
Solution 2 - C#
This is what worked for me:
internal static class ViewHelpers
{
public static JsonSerializerSettings CamelCase
{
get
{
return new JsonSerializerSettings {
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
}
}
}
And then:
[HttpGet]
[Route("api/campaign/list")]
public IHttpActionResult ListExistingCampaigns()
{
var domainResults = _campaignService.ListExistingCampaigns();
return Json(domainResults, ViewHelpers.CamelCase);
}
The class CamelCasePropertyNamesContractResolver
comes from Newtonsoft.Json.dll
in Json.NET library.
Solution 3 - C#
It turns out that
return Json(result);
was the culprit, causing the serialization process to ignore the camelcase setting. And that
return Request.CreateResponse(HttpStatusCode.OK, result, Request.GetConfiguration());
was the droid I was looking for.
Also
json.UseDataContractJsonSerializer = true;
Was putting a spanner in the works and turned out to be NOT the droid I was looking for.
Solution 4 - C#
All the above answers didn't work for me with Owin Hosting and Ninject. Here's what worked for me:
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);
}
/// <summary>
/// Configure all JSON responses to have camel case property names.
/// </summary>
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();
}
}
The key difference is: new HttpConfiguration() rather than GlobalConfiguration.Configuration.
Solution 5 - C#
Code of 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();
}
}
Make sure your API Action Method returns data in following way and you have installed latest version of Json.Net/Newtonsoft.Json Installed:
[HttpGet]
public HttpResponseMessage List()
{
try
{
var result = /*write code to fetch your result - type can be anything*/;
return Request.CreateResponse(HttpStatusCode.OK, result);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
Solution 6 - C#
In your Owin Startup add this line...
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;
}
}
Solution 7 - C#
Here's an obscure one, when the route attribute did not match the GET url but the GET url matched the method name, the jsonserializer camel case directive would be ignored e.g.
> http://website/api/geo/geodata
//uppercase fail cakes
[HttpGet]
[Route("countries")]
public async Task<GeoData> GeoData()
{
return await geoService.GetGeoData();
}
//lowercase nomnomnom cakes
[HttpGet]
[Route("geodata")]
public async Task<GeoData> GeoData()
{
return await geoService.GetGeoData();
}
Solution 8 - C#
I have solved it following ways.
[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<ItemInfo> itemList = GetItemsFromDatabase(moduleId);
return Request.CreateResponse(HttpStatusCode.OK, itemList, config);
}
catch (System.Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
Solution 9 - C#
I'm using WebApi with Breeze and I ran the same issue when trying to execute a non-breeze action into a breeze controller. I tried to use the apprach Request.GetConfiguration but the same result. So, when I access the object returned by Request.GetConfiguration I realize that the serializer used by request is the one that breeze-server use to make it's magic. Any way, I resolved my issue creating a different HttpConfiguration:
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;
}
}
and passing it as parameter at Request.CreateResponse as follow:
return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);