Web API 2: 如何在对象及其子对象上返回具有 camelCased 属性名的 JSON

更新

谢谢你的回答。我正在进行一个新项目,看起来我终于搞清楚了这个问题: 看起来下面的代码实际上是罪魁祸首:

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

其他地方。

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");
}

假设它是 WebAPI,我忽略了看起来无害的 JsonContent,但事实并非如此。

这是用过的 abc 0... 我能第一个说吗,卧槽?或者应该是“他们为什么要这么做?”


最初的问题如下

有人会认为这将是一个简单的配置设置,但它逃避我太长时间了。

我研究了各种解决方案和答案:

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

似乎不适用于最新的 WebAPI 版本..。

下面的命令似乎不起作用-属性名仍然是 PascalCased。

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;


json.UseDataContractJsonSerializer = true;
json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;


json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

Mayank 在这里的回答是: WebAPI 子对象(嵌套对象、子对象)似乎是一个不令人满意但可行的答案,直到我意识到这些属性必须添加到生成的代码中,因为我们正在使用 linq2sql..。

有什么办法可以自动做到这一点吗? 这种“肮脏的”已经困扰了我很长一段时间了。

99847 次浏览

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.

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;
}

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.

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.

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();
}

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);
}
}

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);
}
}

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);

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;
}
}