API Web 2: come restituire JSON con nomi di proprietà camelCased, sugli oggetti e sui loro sotto-oggetti


104

AGGIORNARE

Grazie per tutte le risposte. Sono su un nuovo progetto e sembra che finalmente sia arrivato in fondo a questo: sembra che il seguente codice fosse in effetti la colpa:

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

altrove...

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

Avevo trascurato l'innocuo JsonContent supponendo che fosse WebAPI ma no.

Questo è usato ovunque ... Posso essere il primo a dire, wtf? O forse dovrebbe essere "Perché lo fanno?"


segue la domanda originale

Si sarebbe pensato che questa fosse una semplice impostazione di configurazione, ma ormai mi è sfuggita troppo a lungo.

Ho esaminato varie soluzioni e risposte:

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

non sembra applicarsi all'ultima versione di WebAPI ...

Quanto segue non sembra funzionare - i nomi delle proprietà sono ancora PascalCased.

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;

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

json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

La risposta di Mayank qui: CamelCase JSON WebAPI Sub-Objects (oggetti annidati, oggetti figlio) sembrava una risposta insoddisfacente ma praticabile fino a quando non ho capito che questi attributi avrebbero dovuto essere aggiunti al codice generato poiché stiamo usando linq2sql ...

Qualche modo per farlo automaticamente? Questo "cattivo" mi tormenta da molto tempo ormai.



Inoltre c'è una ragione per cui Linq2SQL produce classi parziali. Inoltre ... Linq2SQL WTF ?!
Aron

1
Grazie, ma questo collegamento è per MVC, è l'API Web 2 che sto utilizzando e non sono sicuro che esista un modo per impostare il tipo di contenuto in questo modo e restituire una stringa, ma se c'è non sembra come la soluzione giusta .. Grazie per il suggerimento anche sulle classi parziali, ma è possibile aggiungere un attributo a una proprietà definita nell'altra parte del parziale?
Tom,

Inoltre sì, linq2sql wtf ... non è una mia decisione :)
Tom

il risultato è lo stesso, l'unica differenza è dove si inietta il JsonSerializer. stackoverflow.com/questions/13274625/…
Aron

Risposte:


175

Mettendo tutto insieme ottieni ...

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

Sicuramente il modo per accenderlo, ma il mio problema era che questa impostazione veniva ignorata (vedi la mia risposta)
Tom

1
@ Tom erm ... Tom sapevi cosa json.UseDataContractJsonSerializer = true;fa? Indica a WebAPI di non utilizzare Json.Netper la serializzazione. > _ <
Aron

Sì, adesso lo so. Tuttavia, c'era anche un ulteriore problema. L'ho verificato. Vedi la mia risposta. Vedi anche stackoverflow.com/questions/28552567/…
Tom

1
In effetti, a un esame più attento risulta che mi sbagliavo nella mia conclusione precedente. Vedi il mio aggiornamento.
Tom

28

Questo è ciò che ha funzionato per me:

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

E poi:

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

La classe CamelCasePropertyNamesContractResolverviene da Newtonsoft.Json.dllin Json.NET biblioteca.


3
Questo approccio è molto utile quando si desidera avere camelCasing solo per alcune API, non per tutte le API nell'applicazione. (Y)
droidbot

15

Si scopre che

return Json(result);

è stato il colpevole, facendo sì che il processo di serializzazione ignori l'impostazione camelcase. E quello

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

era il droide che stavo cercando.

Anche

json.UseDataContractJsonSerializer = true;

Stavo mettendo a punto una chiave inglese e si è rivelato NON essere il droide che stavo cercando.


Questa è in realtà la risposta sbagliata. Vedi il mio aggiornamento nella domanda.
Tom

In realtà ho scoperto che questo è il caso. Al ritorno Json(result), vedevo tutto in PascalCase, ma quando sono tornato Content(StatusCode, result)ha funzionato come previsto.
DeeKayy90

12

Tutte le risposte di cui sopra non hanno funzionato per me con Owin Hosting e Ninject. Ecco cosa ha funzionato per me:

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

La differenza fondamentale è: new HttpConfiguration () anziché GlobalConfiguration.Configuration.


Per l'auto-hosting tramite OWIN, questo è perfetto. Grazie!
Julian Melville

3
Se stai usando Owin, questa soluzione funziona perfettamente, ma solo dopo aver strappato tutti i capelli!
Alastair

10

Codice di 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();

        }
    }


Assicurati che il tuo metodo di azione API restituisca i dati nel modo seguente e di aver installato l'ultima versione di 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

Nella tua Owin Startup aggiungi questa riga ...

 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

Eccone uno oscuro, quando l'attributo route non corrisponde all'URL GET ma l'URL GET corrisponde al nome del metodo, la direttiva jsonserializer camel case verrà ignorata ad es.

http: // sito / api / geo / geodati

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

L'ho risolto seguendo modi.

[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

Sto usando WebApi con Breeze e ho riscontrato lo stesso problema durante il tentativo di eseguire un'azione non-breeze in un controller breeze. Ho provato a utilizzare l'apprach Request.GetConfiguration ma lo stesso risultato. Quindi, quando accedo all'oggetto restituito da Request.GetConfiguration mi rendo conto che il serializzatore utilizzato da request è quello che breeze-server usa per renderlo magico. Ad ogni modo, ho risolto il mio problema creando una diversa 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;
            }
        }

e passandolo come parametro in Request.CreateResponse come segue:

return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.