Веб-API 2: как вернуть JSON с именами свойств camelCased для объектов и их подобъектов

104

ОБНОВИТЬ

Спасибо за ответы на все вопросы. Я работаю над новым проектом, и, похоже, я наконец дошел до сути: похоже, что на самом деле виноват следующий код:

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

Я упустил из виду безобидно выглядящий JsonContent, предполагая, что это был WebAPI, но нет.

Это используется везде ... Могу я быть первым, кто скажет, черт возьми? Или, возможно, это должно быть "Почему они это делают?"


оригинальный вопрос следует

Можно было подумать, что это простая настройка конфигурации, но сейчас она ускользнула от меня слишком долго.

Я просмотрел различные решения и ответы:

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 здесь: CamelCase JSON WebAPI Sub-Objects (вложенные объекты, дочерние объекты) казались неудовлетворительным, но работоспособным ответом, пока я не понял, что эти атрибуты должны быть добавлены в сгенерированный код, поскольку мы используем linq2sql ...

Как сделать это автоматически? Эта «мерзость» преследует меня уже давно.

Том
источник
Также есть причина, по которой Linq2SQL создает частичные классы. Также ... Linq2SQL WTF ?!
Арон
1
Спасибо, но эта ссылка предназначена для MVC, это веб-API 2, который я использую, и я не уверен, есть ли способ установить такой тип содержимого и вернуть строку, но если есть, это не похоже вроде как вполне правильное решение .. Спасибо и за подсказку о частичных классах, но можно ли добавить атрибут к свойству, определенному в другой части партиала?
Том
Также да, linq2sql wtf ... не мое решение :)
Tom
результат тот же, разница только в том, куда вы вводите JsonSerializer. stackoverflow.com/questions/13274625/…
Арон

Ответы:

175

Собирая все вместе, вы получаете ...

protected void Application_Start()
{
    HttpConfiguration config = GlobalConfiguration.Configuration;
    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
}
Арон
источник
Определенно способ включить его, но моя проблема заключалась в том, что этот параметр игнорировался (см. Мой ответ)
Том
1
@ Том эм ... Том ты знал, что json.UseDataContractJsonSerializer = true;делает? Он сообщает WebAPI не использовать его Json.Netдля сериализации. > _ <
Арон
Да, теперь знаю. Однако была и дополнительная проблема. Я это проверил. Смотрите мой ответ. Также см. Stackoverflow.com/questions/28552567/…
Том
1
На самом деле, при ближайшем рассмотрении оказывается, что я ошибался в своем предыдущем заключении. Смотрите мое обновление.
Том
28

Вот что сработало для меня:

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

А потом:

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

Класс CamelCasePropertyNamesContractResolverприходит из Newtonsoft.Json.dllв Json.NET библиотеке.

феликс-б
источник
3
Этот подход очень полезен, когда нужно иметь camelCasing только для некоторых API, а не для всех API в приложении. (Y)
droidbot
15

Оказывается, что

return Json(result);

был виновником, из-за чего процесс сериализации игнорировал параметр camelcase. И это

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

был дроидом, которого я искал.

Также

json.UseDataContractJsonSerializer = true;

Вкладывал гаечный ключ в работу и оказался НЕ тем дроидом, которого я искал.

Том
источник
На самом деле это неправильный ответ. Смотрите мое обновление в вопросе.
Том
Я действительно обнаружил, что это так. Вернувшись Json(result), я все видел в PascalCase, но когда вернулся, все заработало, Content(StatusCode, result)как ожидалось.
DeeKayy90
12

Все приведенные выше ответы не сработали для меня с Owin Hosting и 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();
    }
}

Ключевое отличие: новый HttpConfiguration (), а не GlobalConfiguration.Configuration.

mkaj
источник
Для самостоятельного хостинга через OWIN это идеально. Спасибо!
Джулиан Мелвилл
3
Если вы используете Owin, этот раствор работает отлично, но только после того, как вы вырвите все волосы!
Alastair
10

Код 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();

        }
    }


Убедитесь, что ваш метод действий API возвращает данные следующим образом, и что вы установили последнюю версию Json.Net/Newtonsoft.Json Installed:

    [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);
        }
    }
Джей Шах
источник
4

В вашем запуске Owin добавьте эту строку ...

 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;
    }
}
smatthews1999
источник
3

Вот непонятный случай: когда атрибут маршрута не соответствует URL-адресу GET, но URL-адрес GET соответствует имени метода, директива jsonserializer camel case игнорируется, например

http: // веб-сайт / 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();
}
Дженсон-кнопка-событие
источник
2

Я решил это следующими способами.

[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);
            }
}
Хадемул Башер
источник
0

Я использую WebApi с Breeze, и я столкнулся с той же проблемой при попытке выполнить действие без ветра в контроллере Breeze. Я попытался использовать apprach Request.GetConfiguration, но результат тот же. Итак, когда я обращаюсь к объекту, возвращаемому Request.GetConfiguration, я понимаю, что сериализатор, используемый запросом, - это тот, который использует 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;
            }
        }

и передав его как параметр в Request.CreateResponse следующим образом:

return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);
Леонардо Ненингер
источник