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

时间:2021-12-28 07:32:58

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.

我忽略了看上去无害的JsonContent,假设它是WebAPI,但不是。

This is used everywhere... Can I just be the first to say, wtf? Or perhaps that should be "Why are they doing this?"

这是使用无处不在…我能不能第一个说,wtf?或者那应该是“他们为什么要这么做?”


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

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

doesn't seem to apply to latest WebAPI version...

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

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

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

Any way to do this automatically? This 'nasty' has plagued me for a long time now.

有办法自动完成吗?这个“讨厌的”困扰我很久了。

9 个解决方案

#1


137  

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

#2


22  

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.

CamelCasePropertyNamesContractResolver类来自Newtonsoft.Json。在Json dll。网络图书馆。

#3


12  

All the above answers didn't work for me with Owin Hosting and Ninject. Here's what worked for me:

以上的答案对Owin主机和Ninject都不起作用。这就是我的工作:

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.

关键的区别是:新的HttpConfiguration()而不是GlobalConfiguration.Configuration。

#4


11  

It turns out that

事实证明,

return Json(result);

was the culprit, causing the serialization process to ignore the camelcase setting. And that

是罪魁祸首,导致序列化过程忽略camelcase设置。这

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.

我在装扳手,结果不是我要找的机器人。

#5


6  

Code of WebApiConfig:

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:

确保您的API操作方法以以下方式返回数据,并且您已经安装了最新版本的Json.Net/Newtonsoft。Json安装:

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

#6


4  

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

#7


3  

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.

这里有一个模糊的例子,当route属性与GET url不匹配但GET url与方法名匹配时,jsonserializer驼峰指令将被忽略,例如。

http://website/api/geo/geodata

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

#8


1  

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

#9


0  

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:

我正在使用带有Breeze的WebApi,在尝试对Breeze控制器执行非Breeze操作时,我遇到了同样的问题。我试图使用apprach请求。GetConfiguration但是结果是相同的。当我访问请求返回的对象时。GetConfiguration我意识到request使用的序列化器是breeze-server用来使其具有魔力的。无论如何,我解决了创建不同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:

并根据需要将其作为参数传递。连接CreateResponse遵循:

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

#1


137  

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

#2


22  

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.

CamelCasePropertyNamesContractResolver类来自Newtonsoft.Json。在Json dll。网络图书馆。

#3


12  

All the above answers didn't work for me with Owin Hosting and Ninject. Here's what worked for me:

以上的答案对Owin主机和Ninject都不起作用。这就是我的工作:

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.

关键的区别是:新的HttpConfiguration()而不是GlobalConfiguration.Configuration。

#4


11  

It turns out that

事实证明,

return Json(result);

was the culprit, causing the serialization process to ignore the camelcase setting. And that

是罪魁祸首,导致序列化过程忽略camelcase设置。这

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.

我在装扳手,结果不是我要找的机器人。

#5


6  

Code of WebApiConfig:

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:

确保您的API操作方法以以下方式返回数据,并且您已经安装了最新版本的Json.Net/Newtonsoft。Json安装:

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

#6


4  

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

#7


3  

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.

这里有一个模糊的例子,当route属性与GET url不匹配但GET url与方法名匹配时,jsonserializer驼峰指令将被忽略,例如。

http://website/api/geo/geodata

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

#8


1  

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

#9


0  

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:

我正在使用带有Breeze的WebApi,在尝试对Breeze控制器执行非Breeze操作时,我遇到了同样的问题。我试图使用apprach请求。GetConfiguration但是结果是相同的。当我访问请求返回的对象时。GetConfiguration我意识到request使用的序列化器是breeze-server用来使其具有魔力的。无论如何,我解决了创建不同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:

并根据需要将其作为参数传递。连接CreateResponse遵循:

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