Web-API 2: Zurückgeben von JSON mit camelCased-Eigenschaftsnamen für Objekte und deren Unterobjekte


104

AKTUALISIEREN

Danke für alle Antworten. Ich bin in einem neuen Projekt und es sieht so aus, als wäre ich endlich auf den Grund gegangen: Es sieht so aus, als ob der folgende Code tatsächlich schuld war:

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

anderswo...

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

Ich hatte den harmlos aussehenden JsonContent übersehen, vorausgesetzt, es war WebAPI, aber nein.

Dies wird überall verwendet ... Kann ich nur der Erste sein, der sagt, wtf? Oder vielleicht sollte das lauten: "Warum machen sie das?"


ursprüngliche Frage folgt

Man hätte gedacht, dass dies eine einfache Konfigurationseinstellung wäre, aber sie ist mir jetzt zu lange entgangen.

Ich habe mir verschiedene Lösungen und Antworten angesehen:

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

scheint nicht auf die neueste WebAPI-Version zuzutreffen ...

Folgendes scheint nicht zu funktionieren - Eigenschaftsnamen sind immer noch PascalCased.

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;

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

json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

Mayanks Antwort hier: CamelCase JSON WebAPI- Unterobjekte (verschachtelte Objekte, untergeordnete Objekte) schienen eine unbefriedigende, aber praktikable Antwort zu sein, bis mir klar wurde, dass diese Attribute dem generierten Code hinzugefügt werden müssen, da wir linq2sql verwenden ...

Wie kann man das automatisch machen? Dieses "böse" hat mich schon lange geplagt.



Es gibt auch einen Grund, warum Linq2SQL Teilklassen erzeugt. Auch ... Linq2SQL WTF?!
Aron

1
Vielen Dank, aber dieser Link ist für MVC, es ist die Web-API 2, die ich verwende, und ich bin nicht sicher, ob es eine Möglichkeit gibt, den Inhaltstyp wie folgt festzulegen und eine Zeichenfolge zurückzugeben, aber wenn es eine gibt, scheint es nicht so wie ganz die richtige Lösung. Danke auch für den Tipp zu Teilklassen, aber ist es möglich, einer Eigenschaft, die im anderen Teil des Teils definiert ist, ein Attribut hinzuzufügen?
Tom

Auch ja, linq2sql wtf ... nicht meine Entscheidung :)
Tom

Das Ergebnis ist das gleiche. Der einzige Unterschied besteht darin, wo Sie das injizieren JsonSerializer. stackoverflow.com/questions/13274625/…
Aron

Antworten:


175

Wenn Sie alles zusammenfügen, erhalten Sie ...

protected void Application_Start()
{
    HttpConfiguration config = GlobalConfiguration.Configuration;
    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
}

Auf jeden Fall die Möglichkeit, es einzuschalten, aber mein Problem war, dass diese Einstellung ignoriert wurde (siehe meine Antwort)
Tom

1
@ Tom erm ... Tom wusstest du was json.UseDataContractJsonSerializer = true;macht? Es weist WebAPI an, nicht Json.Netfür die Serialisierung zu verwenden. > _ <
Aron

Ja, das tue ich jetzt. Es gab jedoch auch ein zusätzliches Problem. Ich habe das überprüft. Siehe meine Antwort. Siehe auch stackoverflow.com/questions/28552567/…
Tom

1
Bei näherer Betrachtung stellt sich heraus, dass ich mich in meiner früheren Schlussfolgerung geirrt habe. Siehe mein Update.
Tom

28

Das hat bei mir funktioniert:

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

Und dann:

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

Die Klasse CamelCasePropertyNamesContractResolverkommt aus Newtonsoft.Json.dllin Json.NET Bibliothek.


3
Dieser Ansatz ist sehr nützlich, wenn camelCasing nur für einige APIs und nicht für alle APIs in der Anwendung verwendet werden soll. (Y)
Droidbot

15

Es stellt sich heraus, dass

return Json(result);

war der Schuldige, was dazu führte, dass der Serialisierungsprozess die Camelcase-Einstellung ignorierte. Und das

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

war der Droide, den ich suchte.

Ebenfalls

json.UseDataContractJsonSerializer = true;

Ich habe einen Schraubenschlüssel in die Arbeit gesteckt und mich als NICHT den Droiden herausgestellt, den ich gesucht habe.


Das ist eigentlich die falsche Antwort. Siehe mein Update in der Frage.
Tom

Ich fand das tatsächlich so. Bei Json(result)meiner Rückkehr sah ich alles in PascalCase, aber als ich zurückkam Content(StatusCode, result), funktionierte es wie erwartet.
DeeKayy90

12

Alle oben genannten Antworten haben bei Owin Hosting und Ninject nicht funktioniert. Folgendes hat bei mir funktioniert:

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

Der Hauptunterschied ist: new HttpConfiguration () anstelle von GlobalConfiguration.Configuration.


Für das Selbsthosting über OWIN ist dies perfekt. Vielen Dank!
Julian Melville

3
Wenn Sie Owin verwenden, funktioniert diese Lösung perfekt, aber erst, nachdem Sie alle Haare ausgerissen haben!
Alastair

10

Code von 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();

        }
    }


Stellen Sie sicher, dass Ihre API-Aktionsmethode Daten auf folgende Weise zurückgibt und Sie die neueste Version von Json.Net/Newtonsoft.Json installiert installiert haben:

    [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

Fügen Sie in Ihrem Owin-Startup diese Zeile hinzu ...

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

3

Hier ist eine obskure, wenn das Routenattribut nicht mit der GET-URL übereinstimmt, aber die GET-URL mit dem Methodennamen übereinstimmt, wird die jsonserializer-Kamelfallanweisung ignoriert, z

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

2

Ich habe es auf folgende Weise gelöst.

[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

Ich verwende WebApi mit Breeze und habe das gleiche Problem ausgeführt, als ich versucht habe, eine Non-Breeze-Aktion in einem Breeze-Controller auszuführen. Ich habe versucht, die apprach Request.GetConfiguration zu verwenden, aber das gleiche Ergebnis. Wenn ich also auf das von Request.GetConfiguration zurückgegebene Objekt zugreife, stelle ich fest, dass der von Request verwendete Serializer derjenige ist, den der Breeze-Server verwendet, um seine Magie zu erzeugen. Auf jeden Fall habe ich mein Problem beim Erstellen einer anderen HttpConfiguration behoben:

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

und Übergabe als Parameter bei Request.CreateResponse wie folgt:

return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.