Web API 2: comment renvoyer JSON avec des noms de propriétés camelCased, sur des objets et leurs sous-objets

104

METTRE À JOUR

Merci pour toutes les réponses. Je suis sur un nouveau projet et il semble que je suis enfin arrivé au fond des choses: il semble que le code suivant était en fait à blâmer:

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

autre part...

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

J'avais oublié le JsonContent à l'aspect inoffensif en supposant que c'était WebAPI mais non.

Ceci est utilisé partout ... Puis-je être le premier à dire, wtf? Ou peut-être que cela devrait être "Pourquoi font-ils cela?"


la question originale suit

On aurait pensé que ce serait un simple paramètre de configuration, mais cela m'échappe depuis trop longtemps.

J'ai examiné différentes solutions et réponses:

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

ne semble pas s'appliquer à la dernière version de WebAPI ...

Ce qui suit ne semble pas fonctionner - les noms de propriétés sont toujours en PascalCased.

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;

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

json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

La réponse de Mayank ici: Les sous-objets CamelCase JSON WebAPI (objets imbriqués, objets enfants) semblaient être une réponse insatisfaisante mais réalisable jusqu'à ce que je réalise que ces attributs devraient être ajoutés au code généré car nous utilisons linq2sql ...

Un moyen de le faire automatiquement? Ce «méchant» me tourmente depuis longtemps maintenant.

À M
la source
Il y a aussi une raison pour laquelle Linq2SQL produit des classes partielles. Aussi ... Linq2SQL WTF?!
Aron
1
Merci mais ce lien est pour MVC, c'est l'API Web 2 que j'utilise, et je ne suis pas sûr qu'il existe un moyen de définir le type de contenu comme celui-ci et de renvoyer une chaîne, mais s'il y en a, cela ne semble pas comme tout à fait la bonne solution .. Merci pour le conseil sur les classes partielles aussi, mais est-il possible d'ajouter un attribut à une propriété définie dans l'autre partie du partiel?
Tom
Aussi oui, linq2sql wtf ... pas ma décision :)
Tom
le résultat est le même, la seule différence est l'endroit où vous injectez le fichier JsonSerializer. stackoverflow.com/questions/13274625/…
Aron

Réponses:

175

En mettant tout cela ensemble, vous obtenez ...

protected void Application_Start()
{
    HttpConfiguration config = GlobalConfiguration.Configuration;
    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
}
Aron
la source
Certainement la façon de l'activer, mais mon problème était que ce paramètre était ignoré (voir ma réponse)
Tom
1
@Tom erm ... Tom saviez-vous ce que json.UseDataContractJsonSerializer = true;fait? Il indique à WebAPI de ne pas l'utiliser Json.Netpour la sérialisation. > _ <
Aron
Oui, je le fais maintenant. Cependant, il y avait aussi un problème supplémentaire. J'ai vérifié cela. Voyez ma réponse. Voir aussi stackoverflow.com/questions/28552567/…
Tom
1
En fait, en y regardant de plus près, il s'avère que je me suis trompé dans ma conclusion précédente. Voir ma mise à jour.
Tom
28

C'est ce qui a fonctionné pour moi:

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

Puis:

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

La classe CamelCasePropertyNamesContractResolvervient Newtonsoft.Json.dlldans Json.NET bibliothèque.

félix-b
la source
3
Cette approche est très utile lorsque l'on veut avoir le camelCasing uniquement pour certaines API, pas pour toutes les API de l'application. (Y)
droidbot
15

Il se trouve que

return Json(result);

était le coupable, ce qui a amené le processus de sérialisation à ignorer le paramètre camelcase. Et cela

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

était le droïde que je cherchais.

Aussi

json.UseDataContractJsonSerializer = true;

Mettait une clé dans les travaux et s'est avéré être PAS le droïde que je recherchais.

À M
la source
C'est en fait la mauvaise réponse. Voir ma mise à jour dans la question.
Tom
J'ai trouvé que c'était le cas. En revenant Json(result), je voyais tout dans PascalCase, mais quand je suis revenu, Content(StatusCode, result)cela fonctionnait comme prévu.
DeeKayy90
12

Toutes les réponses ci-dessus n'ont pas fonctionné pour moi avec Owin Hosting et Ninject. Voici ce qui a fonctionné pour moi:

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 principale différence est: new HttpConfiguration () plutôt que GlobalConfiguration.Configuration.

mkaj
la source
Pour l'auto-hébergement via OWIN, c'est parfait. Merci!
Julian Melville
3
Si vous utilisez Owin, cette solution fonctionne parfaitement, mais seulement après avoir déchiré tous vos cheveux!
Alastair le
10

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

        }
    }


Assurez-vous que votre méthode d'action API renvoie les données de la manière suivante et que vous avez installé la dernière version de 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
la source
4

Dans votre Owin Startup, ajoutez cette ligne ...

 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
la source
3

En voici un obscur, lorsque l'attribut route ne correspondait pas à l'url GET mais que l'url GET correspondait au nom de la méthode, la directive jsonserializer camel case serait ignorée, par exemple

http: // site Web / 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();
}
événement jenson-button
la source
2

Je l'ai résolu de différentes manières.

[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
la source
0

J'utilise WebApi avec Breeze et j'ai rencontré le même problème en essayant d'exécuter une action non-Breeze dans un contrôleur Breeze. J'ai essayé d'utiliser le Request.GetConfiguration d'apprach mais le même résultat. Ainsi, lorsque j'accède à l'objet renvoyé par Request.GetConfiguration, je me rends compte que le sérialiseur utilisé par request est celui que breeze-server utilise pour faire de la magie. Quoi qu'il en soit, j'ai résolu mon problème en créant une configuration HttpConfiguration différente:

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

et en le passant comme paramètre à Request.CreateResponse comme suit:

return this.Request.CreateResponse(HttpStatusCode.OK, result, WebApiHelper.BreezeControllerCamelCase);
Leonardo Neninger
la source