Web API 2: jak zwrócić kod JSON z nazwami właściwości camelCased na obiektach i ich obiektach podrzędnych

104

AKTUALIZACJA

Dzięki za wszystkie odpowiedzi. Jestem nad nowym projektem i wygląda na to, że w końcu dotarłem do sedna tego: Wygląda na to, że w rzeczywistości winien był następujący kod:

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

gdzie indziej...

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

Przeoczyłem niewinnie wyglądający JsonContent, zakładając, że to WebAPI, ale nie.

To jest używane wszędzie ... Czy mogę być pierwszym, który powie, wtf? A może powinno to brzmieć „Dlaczego oni to robią?”


pojawia się oryginalne pytanie

Można by pomyśleć, że to proste ustawienie konfiguracji, ale teraz umykało mi to zbyt długo.

Patrzyłem na różne rozwiązania i odpowiedzi:

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

wydaje się, że nie ma zastosowania do najnowszej wersji WebAPI ...

Poniższe wydaje się nie działać - nazwy właściwości są nadal PascalCased.

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;

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

json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

Odpowiedź Mayanka tutaj: Podobiekty CamelCase JSON WebAPI (obiekty zagnieżdżone, obiekty potomne) wydawały się niezadowalającą, ale wykonalną odpowiedzią, dopóki nie zdałem sobie sprawy, że te atrybuty będą musiały zostać dodane do wygenerowanego kodu, ponieważ używamy linq2sql ...

Jakiś sposób, aby zrobić to automatycznie? To „paskudne” nęka mnie od dawna.

Tomek
źródło
Istnieje również powód, dla którego Linq2SQL tworzy klasy częściowe. Poza tym ... Linq2SQL WTF ?!
Aron,
1
Dzięki, ale ten link jest do MVC, używam interfejsu API sieci Web 2 i nie jestem pewien, czy istnieje sposób na ustawienie typu zawartości w ten sposób i zwrócenie ciągu znaków, ale jeśli istnieje, nie wydaje się jak całkiem nieźle. Dzięki za podpowiedź o klasach częściowych, ale czy jest możliwe dodanie atrybutu do właściwości zdefiniowanej w drugiej części podrzędnej?
Tom
Również tak, linq2sql wtf ... nie moja decyzja :)
Tom
wynik jest taki sam, jedyną różnicą jest miejsce wstrzyknięcia JsonSerializer. stackoverflow.com/questions/13274625/…
Aron,

Odpowiedzi:

175

Łącząc to wszystko razem, otrzymujesz ...

protected void Application_Start()
{
    HttpConfiguration config = GlobalConfiguration.Configuration;
    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
}
Aron
źródło
Zdecydowanie sposób na włączenie go, ale moim problemem było to, że to ustawienie było ignorowane (zobacz moją odpowiedź)
Tom,
1
@Tom erm ... Tom, czy wiesz, co json.UseDataContractJsonSerializer = true;robi? Mówi WebAPI, aby nie używać go Json.Netdo serializacji. > _ <
Aron,
Tak, teraz rozumiem. Jednak był też dodatkowy problem. Sprawdziłem to. Zobacz moją odpowiedź. Zobacz także stackoverflow.com/questions/28552567/…
Tom
1
W rzeczywistości po bliższym przyjrzeniu się okazuje się, że pomyliłem się w swoim wcześniejszym wniosku. Zobacz moją aktualizację.
Tom
28

Oto, co zadziałało dla mnie:

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

I wtedy:

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

Klasa CamelCasePropertyNamesContractResolverpochodzi Newtonsoft.Json.dllw Json.NET bibliotece.

felix-b
źródło
3
Takie podejście jest bardzo przydatne, gdy chce się mieć camelCasing tylko dla niektórych interfejsów API, a nie dla wszystkich interfejsów API w aplikacji. (Y)
droidbot
15

Okazało się, że

return Json(result);

był winowajcą, powodując, że proces serializacji ignorował ustawienie camelcase. I to

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

był droidem, którego szukałem.

Również

json.UseDataContractJsonSerializer = true;

Wkładałem klucz do robót i okazało się, że NIE jest to robot, którego szukałem.

Tomek
źródło
To właściwie zła odpowiedź. Zobacz moją aktualizację w pytaniu.
Tom
Właściwie stwierdziłem, że tak jest. Po powrocie Json(result)widziałem wszystko w PascalCase, ale kiedy wróciłem Content(StatusCode, result), działało zgodnie z oczekiwaniami.
DeeKayy90
12

Wszystkie powyższe odpowiedzi nie zadziałały dla mnie z Owin Hosting i Ninject. Oto, co zadziałało dla mnie:

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

Kluczowa różnica to: nowa HttpConfiguration () zamiast GlobalConfiguration.Configuration.

mkaj
źródło
W przypadku samodzielnego hostowania przez OWIN jest to idealne rozwiązanie. Dzięki!
Julian Melville
3
Jeśli stosujesz Owin, to rozwiązanie działa idealnie, ale dopiero po wyrwaniu wszystkich włosów!
Alastair
10

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

        }
    }


Upewnij się, że metoda akcji interfejsu API zwraca dane w następujący sposób i masz zainstalowaną najnowszą wersję 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);
        }
    }
Jay Shah
źródło
4

W swoim Owin Startup dodaj tę linię ...

 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
źródło
3

Oto niejasny, gdy atrybut route nie pasuje do adresu URL GET, ale adres URL GET pasuje do nazwy metody, dyrektywa jsonserializer camel case zostanie zignorowana, np.

http: // witryna internetowa / 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();
}
jenson-button-event
źródło
2

Rozwiązałem to w następujący sposób.

[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);
            }
}
Khademul Basher
źródło
0

Używam WebApi z Breeze i napotkałem ten sam problem, gdy próbowałem wykonać akcję niepowściągliwą w kontrolerze breeze. Próbowałem użyć metody Request.GetConfiguration, ale ten sam wynik. Tak więc, kiedy uzyskuję dostęp do obiektu zwróconego przez Request.GetConfiguration zdaję sobie sprawę, że serializator używany przez request jest tym, którego używa breeze-server do stworzenia magii. W każdym razie rozwiązałem problem, tworząc inną konfigurację 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;
            }
        }

i przekazując go jako parametr w Request.CreateResponse w następujący sposób:

return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);
Leonardo Neninger
źródło