Comment puis-je renvoyer camelCase JSON sérialisé par JSON.NET à partir des méthodes de contrôleur ASP.NET MVC?

246

Mon problème est que je souhaite renvoyer les données JSON camelCased (par opposition à PascalCase standard) via ActionResult s à partir des méthodes de contrôleur ASP.NET MVC, sérialisées par JSON.NET .

À titre d'exemple, considérons la classe C # suivante:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Par défaut, lors du retour d'une instance de cette classe à partir d'un contrôleur MVC en tant que JSON, elle sera sérialisée de la manière suivante:

{
  "FirstName": "Joe",
  "LastName": "Public"
}

Je voudrais qu'il soit sérialisé (par JSON.NET) en tant que:

{
  "firstName": "Joe",
  "lastName": "Public"
}

Comment puis-je faire cela?

aknuds1
la source

Réponses:

389

ou, mettez simplement:

JsonConvert.SerializeObject(
    <YOUR OBJECT>, 
    new JsonSerializerSettings 
    { 
        ContractResolver = new CamelCasePropertyNamesContractResolver() 
    });

Par exemple:

return new ContentResult
{
    ContentType = "application/json",
    Content = JsonConvert.SerializeObject(new { content = result, rows = dto }, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }),
    ContentEncoding = Encoding.UTF8
};
WebDever
la source
2
Ceci est cependant plus complexe à utiliser, car vous devez configurer un ContentResult pour chaque méthode de contrôleur.
aknuds1
2
Oui, je comprends que votre réponse était une solution réutilisable, mon point est de préciser que ce n'est qu'un paramètre de la méthode Serialize.
WebDever du
1
Si vous retournez JSON à partir d'une Controllerméthode, vous devriez probablement utiliser un ApiController, auquel cas cette réponse fonctionne très bien.
Simon Hartcher
1
@SimonHartcher Considérez cependant la portée de la question, pas le cas général.
aknuds1
1
Le type de contenu valide pour JSON n'est application/jsonpas text/plain.
Fred
94

J'ai trouvé une excellente solution à ce problème sur le blog de Mats Karlsson . La solution consiste à écrire une sous-classe d'ActionResult qui sérialise les données via JSON.NET, en configurant ce dernier pour suivre la convention camelCase:

public class JsonCamelCaseResult : ActionResult
{
    public JsonCamelCaseResult(object data, JsonRequestBehavior jsonRequestBehavior)
    {
        Data = data;
        JsonRequestBehavior = jsonRequestBehavior;
    }

    public Encoding ContentEncoding { get; set; }

    public string ContentType { get; set; }

    public object Data { get; set; }

    public JsonRequestBehavior JsonRequestBehavior { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.");
        }

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data == null)
            return;

        var jsonSerializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
        response.Write(JsonConvert.SerializeObject(Data, jsonSerializerSettings));
    }
}

Utilisez ensuite cette classe comme suit dans votre méthode de contrôleur MVC:

public ActionResult GetPerson()
{
    return new JsonCamelCaseResult(new Person { FirstName = "Joe", LastName = "Public" }, JsonRequestBehavior.AllowGet)};
}
aknuds1
la source
3
Réponse parfaite: propre et réutilisable! Je vous remercie.
ponceuse
1
Alors que cette solution fonctionne toujours. mais il a été suggéré 4 ans en arrière. Avons-nous une meilleure solution?
SharpCoder
59

Pour WebAPI , consultez ce lien: http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx

Fondamentalement, ajoutez ce code à votre Application_Start:

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Assaf S.
la source
4
L'API Web et MVC ont été fusionnés dans ASP.NET 6
AlexFoxGill
1
Lien pour plus de commodité; cette configuration fonctionne très bien avec cette réponse: stackoverflow.com/a/26068063/398630 (question différente, mais je les utilise ensemble, et ce lien pourrait me sauver, ainsi que d'autres, de googler à l'avenir).
BrainSlugs83
37

Je pense que c'est la réponse simple que vous recherchez. C'est du blog de Shawn Wildermuth :

// Add MVC services to the services container.
services.AddMvc()
  .AddJsonOptions(opts =>
  {
    opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  });
Quantium
la source
2
Mes excuses, les gars. J'ai lu cette publication trop rapidement. C'est pour ASP.NET 5.
Quantium
8
Ironiquement, je suis venu ici à la recherche d'une réponse à la question à laquelle vous avez répondu ici, alors même si ce n'était pas la réponse à la question du PO, cela m'a quand même aidé. Merci! :)
porcus
1
J'appuie ce que @porcus a dit! Merci @Quantium!
Gromer
4
fyi Pour ASP.NET Core 1.0, c'est un étui à chameau par défaut OOTB
Chris Marisic
3
Il s'avère que ce n'est pas (exactement) la valeur par défaut pour .NET Core 1.0 après tout. Cette solution affecte les propriétés dynamiques et celles-ci ne sont pas affectées par défaut. stackoverflow.com/questions/41329279/…
Niels Brinch
13

Une alternative au filtre personnalisé consiste à créer une méthode d'extension pour sérialiser tout objet en JSON.

public static class ObjectExtensions
{
    /// <summary>Serializes the object to a JSON string.</summary>
    /// <returns>A JSON string representation of the object.</returns>
    public static string ToJson(this object value)
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Converters = new List<JsonConverter> { new StringEnumConverter() }
        };

        return JsonConvert.SerializeObject(value, settings);
    }
}

Appelez-le ensuite au retour de l'action du contrôleur.

return Content(person.ToJson(), "application/json");
Stuart Hallows
la source
Élégant et simple.
markau
1
Vous pouvez même transférer les paramètres vers un champ en lecture seule statique et ajouter une méthode complémentaire FromJson.
Vapor in the Alley
8

Plus simple, c'est mieux IMO!

Pourquoi tu ne fais pas ça?

public class CourseController : JsonController
{
    public ActionResult ManageCoursesModel()
    {
        return JsonContent(<somedata>);
    }
}

Le contrôleur de classe de base simple

public class JsonController : BaseController
{
    protected ContentResult JsonContent(Object data)
    {
        return new ContentResult
        {
            ContentType = "application/json",
             Content = JsonConvert.SerializeObject(data, new JsonSerializerSettings { 
              ContractResolver = new CamelCasePropertyNamesContractResolver() }),
            ContentEncoding = Encoding.UTF8
        };
    }
}
jwize
la source
7

Dans ASP.NET Core MVC.

    public IActionResult Foo()
    {
        var data = GetData();

        var settings = new JsonSerializerSettings 
        { 
            ContractResolver = new CamelCasePropertyNamesContractResolver() 
        });

        return Json(data, settings);
    }
Fred
la source
Et encore mieux, mettez-le dans le fichier Startup.cs.
FatAlbert
6

Vous trouverez ci-dessous une méthode d'action qui renvoie une chaîne json (cameCase) en sérialisant un tableau d'objets.

public string GetSerializedCourseVms()
    {
        var courses = new[]
        {
            new CourseVm{Number = "CREA101", Name = "Care of Magical Creatures", Instructor ="Rubeus Hagrid"},
            new CourseVm{Number = "DARK502", Name = "Defence against dark arts", Instructor ="Severus Snape"},
            new CourseVm{Number = "TRAN201", Name = "Transfiguration", Instructor ="Minerva McGonal"}
        };
        var camelCaseFormatter = new JsonSerializerSettings();
        camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
        return JsonConvert.SerializeObject(courses, camelCaseFormatter);
    }

Notez que l'instance JsonSerializerSettings est passée en tant que deuxième paramètre. C'est ce qui fait que le camelCase se produit.

DanKodi
la source
4

J'ai aimé ça:

public static class JsonExtension
{
    public static string ToJson(this object value)
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Serialize
        };
        return JsonConvert.SerializeObject(value, settings);
    }
}

c'est une méthode d'extension simple dans le noyau MVC, cela va donner la capacité ToJson () à chaque objet de votre projet, à mon avis dans un projet MVC, la plupart des objets devraient avoir la capacité de devenir json, bien sûr, cela dépend :)

Ali Alp
la source
Envisagez d'extraire la variable "settings" en dehors de la méthode (en tant que champ statique privé "camelCaseSettings") afin de ne pas initialiser une nouvelle variable à chaque appel de la méthode ToJson.
Ekus
4

Vous devez définir les paramètres dans le fichier 'Startup.cs'

Vous devez également le définir dans les valeurs par défaut de JsonConvert, c'est si vous souhaitez ultérieurement utiliser directement la bibliothèque pour sérialiser un objet.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(options => {
                options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
    }
Daniel Sánchez
la source
Notez que cette réponse est correcte pour ASP.NET Core, mais pas ASP.NET (qui est le cadre de la question).
Nate Barbettini
0

Si vous renvoyez ActionResult dans l'API Web .net core ou le résultat IHttpAction, vous pouvez simplement envelopper votre modèle dans une méthode Ok () qui correspondra à la casse sur votre frontal et le sérialisera pour vous. Pas besoin d'utiliser JsonConvert. :)

LukePerrin
la source