Forcer les noms de propriétés en minuscules à partir de Json () dans ASP.NET MVC

89

Compte tenu de la classe suivante,

public class Result
{      
    public bool Success { get; set; }

    public string Message { get; set; }
}

Je retourne l'un de ceux-ci dans une action de contrôleur comme ceci,

return Json(new Result() { Success = true, Message = "test"})

Cependant, mon framework côté client s'attend à ce que ces propriétés soient un succès et un message en minuscule. Sans avoir réellement à avoir des noms de propriétés en minuscules, est-ce un moyen de réaliser cette pensée l'appel de fonction Json normal?

James Hughes
la source

Réponses:

130

Le moyen d'y parvenir est d'implémenter un personnalisé JsonResultcomme ici: Création d'un ValueType personnalisé et sérialisation avec un JsonResult personnalisé (lien d'origine mort) .

Et utilisez un sérialiseur alternatif tel que JSON.NET , qui prend en charge ce type de comportement, par exemple:

Product product = new Product
{
  ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc),
  Name = "Widget",
  Price = 9.99m,
  Sizes = new[] {"Small", "Medium", "Large"}
};

string json = 
  JsonConvert.SerializeObject(
    product,
    Formatting.Indented,
    new JsonSerializerSettings 
    { 
      ContractResolver = new CamelCasePropertyNamesContractResolver() 
    }
);

Résulte en

{
  "name": "Widget",
  "expiryDate": "\/Date(1292868060000)\/",
  "price": 9.99,
  "sizes": [
    "Small",
    "Medium",
    "Large"
  ]
}
James Hughes
la source
1
Ce lien fonctionne: james.newtonking.com/json/help/index.html?topic=html/…
Josef Engelfrost
Si vous utilisez JSON.NET et que vous ne voulez pas camelCase mais snake_case, jetez un coup d'œil à l'essentiel, cela m'a vraiment aidé! gist.github.com/crallen/9238178
Niclas Lindqvist
Comment désérialiser? Ex. de "petit" à "petit"
tour
1
@NiclasLindqvist Pour les versions JSON.NET modernes, il existe un moyen beaucoup plus simple d'obtenir snake_case: newtonsoft.com/json/help/html/NamingStrategySnakeCase.htm
Søren Boisen
16

Changer de sérialiseur est simple si vous utilisez l'API Web, mais malheureusement MVC lui-même l'utilise JavaScriptSerializersans option pour le changer pour utiliser JSON.Net.

La réponse de James et la réponse de Daniel vous donnent la flexibilité de JSON.Net, mais signifie que partout où vous auriez normalement return Json(obj)besoin de changer pour return new JsonNetResult(obj)ou similaire, ce qui, si vous avez un grand projet, pourrait s'avérer un problème, et n'est pas non plus très flexible si vous changez d'avis sur le sérialiseur que vous souhaitez utiliser.


J'ai décidé de suivre la ActionFiltervoie. Le code ci-dessous vous permet d'effectuer n'importe quelle action en utilisant JsonResultet de lui appliquer simplement un attribut pour utiliser JSON.Net (avec des propriétés en minuscules):

[JsonNetFilter]
[HttpPost]
public ActionResult SomeJson()
{
    return Json(new { Hello = "world" });
}

// outputs: { "hello": "world" }

Vous pouvez même configurer cela pour qu'il s'applique automatiquement à toutes les actions (avec seulement la performance mineure d'une isvérification):

FilterConfig.cs

// ...
filters.Add(new JsonNetFilterAttribute());

Le code

public class JsonNetFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is JsonResult == false)
            return;

        filterContext.Result = new CustomJsonResult((JsonResult)filterContext.Result);
    }

    private class CustomJsonResult : JsonResult
    {
        public CustomJsonResult(JsonResult jsonResult)
        {
            this.ContentEncoding = jsonResult.ContentEncoding;
            this.ContentType = jsonResult.ContentType;
            this.Data = jsonResult.Data;
            this.JsonRequestBehavior = jsonResult.JsonRequestBehavior;
            this.MaxJsonLength = jsonResult.MaxJsonLength;
            this.RecursionLimit = jsonResult.RecursionLimit;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet
                && String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                throw new InvalidOperationException("GET not allowed! Change JsonRequestBehavior to AllowGet.");

            var response = context.HttpContext.Response;

            response.ContentType = String.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;

            if (this.ContentEncoding != null)
                response.ContentEncoding = this.ContentEncoding;

            if (this.Data != null)
            {
                var json = JsonConvert.SerializeObject(
                    this.Data,
                    new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        });

                response.Write(json);
            }
        }
    }
}
dav_i
la source
10

Avec ma solution, vous pouvez renommer chaque propriété que vous souhaitez.

J'ai trouvé une partie de la solution ici et sur SO

public class JsonNetResult : ActionResult
    {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }
        public object Data { get; set; }

        public JsonSerializerSettings SerializerSettings { get; set; }
        public Formatting Formatting { get; set; }

        public JsonNetResult(object data, Formatting formatting)
            : this(data)
        {
            Formatting = formatting;
        }

        public JsonNetResult(object data):this()
        {
            Data = data;
        }

        public JsonNetResult()
        {
            Formatting = Formatting.None;
            SerializerSettings = new JsonSerializerSettings();
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            var response = context.HttpContext.Response;
            response.ContentType = !string.IsNullOrEmpty(ContentType)
              ? ContentType
              : "application/json";
            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (Data == null) return;

            var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
            var serializer = JsonSerializer.Create(SerializerSettings);
            serializer.Serialize(writer, Data);
            writer.Flush();
        }
    }

Pour que dans mon contrôleur, je puisse faire ça

        return new JsonNetResult(result);

Dans mon modèle, je peux maintenant avoir:

    [JsonProperty(PropertyName = "n")]
    public string Name { get; set; }

Notez que maintenant, vous devez définir le JsonPropertyAttributesur chaque propriété que vous souhaitez sérialiser.

Daniel
la source
1

Bien que ce soit une vieille question, j'espère que l'extrait de code ci-dessous sera utile aux autres,

Je l'ai fait ci-dessous avec l'API Web MVC5.

public JsonResult<Response> Post(Request request)
    {
        var response = new Response();

        //YOUR LOGIC IN THE METHOD
        //.......
        //.......

        return Json<Response>(response, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() });
    }
pgcan
la source
0

Vous pouvez ajouter ce paramètre à Global.asax, et il fonctionnera partout.

public class Global : HttpApplication
{   
    void Application_Start(object sender, EventArgs e)
    {
        //....
         JsonConvert.DefaultSettings = () =>
         {
             var settings = new JsonSerializerSettings
             {
                 ContractResolver = new CamelCasePropertyNamesContractResolver(),
                 PreserveReferencesHandling = PreserveReferencesHandling.None,
                 Formatting = Formatting.None
             };

             return settings;
         }; 
         //....
     }
}
Maksym Labutin
la source