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.

Tom
quelle
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;
}
Aron
quelle
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.

felix-b
quelle
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.

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

mkaj
quelle
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);
        }
    }
Jay Shah
quelle
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;
    }
}
smatthews1999
quelle
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();
}
jenson-button-event
quelle
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);
            }
}
Khademul Basher
quelle
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);
Leonardo Neninger
quelle