Wymuś nazwy właściwości małymi literami z Json () w ASP.NET MVC

89

Biorąc pod uwagę następującą klasę,

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

    public string Message { get; set; }
}

Zwracam jeden z nich w takiej akcji kontrolera,

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

Jednak moja struktura po stronie klienta oczekuje, że te właściwości będą sukcesem i komunikatem małymi literami. Bez faktycznej konieczności posiadania nazw właściwości pisanych małymi literami, czy jest to sposób na osiągnięcie tej myśli w normalnym wywołaniu funkcji Json?

James Hughes
źródło

Odpowiedzi:

130

Sposobem na osiągnięcie tego jest zaimplementowanie niestandardowego, JsonResulttakiego jak tutaj: utworzenie niestandardowego ValueType i serializacja z niestandardowym JsonResult (oryginalny link nie działa ) .

I użyj alternatywnego serializatora, takiego jak JSON.NET , który obsługuje tego typu zachowanie, np .:

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

Prowadzi do

{
  "name": "Widget",
  "expiryDate": "\/Date(1292868060000)\/",
  "price": 9.99,
  "sizes": [
    "Small",
    "Medium",
    "Large"
  ]
}
James Hughes
źródło
1
Ten link działa: james.newtonking.com/json/help/index.html?topic=html/…
Josef Engelfrost,
Jeśli używasz JSON.NET i nie chcesz camelCase, ale snake_case, zapoznaj się z tym sednem, naprawdę mi pomogło! gist.github.com/crallen/9238178
Niclas Lindqvist
Jak przeprowadzić deserializację? Dawny. od „małego” do „małego”
wieża
1
@NiclasLindqvist W przypadku nowoczesnych wersji JSON.NET istnieje znacznie prostszy sposób na uzyskanie snake_case: newtonsoft.com/json/help/html/NamingStrategySnakeCase.htm
Søren Boisen
17

Zmiana serializatora jest prosta, jeśli używasz interfejsu API sieci Web, ale niestety samo MVC używa JavaScriptSerializerbez opcji zmiany tego, aby używać JSON.Net.

Odpowiedź Jamesa i odpowiedź Daniela zapewniają elastyczność JSON.Net, ale oznaczają, że wszędzie tam, gdzie normalnie byś to zrobił return Json(obj), musisz zmienić na return new JsonNetResult(obj)lub coś podobnego, co w przypadku dużego projektu może okazać się problemem, a także nie jest zbyt elastyczne, jeśli zmienisz zdanie na temat serializatora, którego chcesz użyć.


Postanowiłem pójść tą ActionFiltertrasą. Poniższy kod umożliwia wykonanie dowolnej akcji przy użyciu JsonResulti po prostu zastosowanie do niego atrybutu, aby użyć JSON.Net (z właściwościami małych liter):

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

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

Możesz nawet ustawić to tak, aby automagicznie stosowało się do wszystkich akcji (z tylko niewielkim uderzeniem wydajnościowym podczas isczeku):

FilterConfig.cs

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

Kod

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
źródło
10

Dzięki mojemu rozwiązaniu możesz zmienić nazwę każdej nieruchomości, którą chcesz.

Znalazłem część rozwiązania tutaj i na 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();
        }
    }

Więc w moim kontrolerze mogę to zrobić

        return new JsonNetResult(result);

W moim modelu mogę teraz mieć:

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

Zauważ, że teraz musisz ustawić na JsonPropertyAttributekażdą właściwość, którą chcesz serializować.

Daniel
źródło
1

Chociaż to stare pytanie, mam nadzieję, że poniższy fragment kodu będzie pomocny dla innych,

Zrobiłem poniżej z MVC5 Web API.

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
źródło
0

Możesz dodać to ustawienie do Global.asaxi będzie działać wszędzie.

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
źródło