Web API 2: how to return JSON with camelCased property names, on objects and their sub-objects

97,542

Solution 1

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

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

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

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

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);
        }
    }
Share:
97,542

Related videos on Youtube

Tom
Author by

Tom

Updated on July 08, 2022

Comments

  • Tom
    Tom almost 2 years

    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: 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.

    • Aron
      Aron about 9 years
    • Aron
      Aron about 9 years
      Also there is a reason why Linq2SQL produces partial classes. Also...Linq2SQL WTF?!
    • Tom
      Tom about 9 years
      Thanks but this link is for MVC, it's Web API 2 I'm using, and I'm not sure if there's a way to set the content-type like this, and return a string, but if there is it doesn't seem like quite the right solution.. Thanks for the tip about partial classes too, but is it possible to add an attribute to a property defined in the other part of the partial?
    • Tom
      Tom about 9 years
      Also yes, linq2sql wtf... not my decision :)
    • Aron
      Aron about 9 years
      the result is the same, the only difference is where you inject the JsonSerializer. stackoverflow.com/questions/13274625/…
    • Aron
      Aron about 9 years
      yes it is possible, but not in C#. You'd probably want to achieve it using some sort of compile time AOP like PostSharp or Fody.
  • Tom
    Tom about 9 years
    Definitely the way to turn it on, but my problem was that this setting was being ignored (see my answer)
  • Aron
    Aron about 9 years
    @Tom erm...Tom did you know what json.UseDataContractJsonSerializer = true; does? It tells WebAPI not to use Json.Net for serialization. >_<
  • Tom
    Tom about 9 years
    Yes, I do now. However, there was also an additional problem. I verified this. See my answer. Also see stackoverflow.com/questions/28552567/…
  • Tom
    Tom about 9 years
    This is actually the wrong answer. See my update in the question.
  • Tom
    Tom about 9 years
    In fact, on closer inspection it turns out that I was mistaken in my earlier conclusion. See my update.
  • Julian Melville
    Julian Melville over 8 years
    For self-hosting via OWIN, this is perfect. Thanks!
  • droidbot
    droidbot about 8 years
    This approach is very useful when one wants to have the camelCasing only for some APIs, not for all the APIs in the application. (Y)
  • Alastair
    Alastair about 7 years
    If you are using Owin, this solution works perfectly, but only after you tear all your hair out!
  • DeeKayy90
    DeeKayy90 over 6 years
    I actually found this to be the case. When returning Json(result), I was seeing everything in PascalCase, but when I returned Content(StatusCode, result) it worked as expected.