Jak mogę zwrócić camelCase JSON zserializowany przez JSON.NET z metod kontrolera ASP.NET MVC?

246

Mój problem polega na tym, że chcę zwrócić dane JSON camelCased (w przeciwieństwie do standardowej PascalCase) za pośrednictwem ActionResult s z metod kontrolera ASP.NET MVC, serializowanych przez JSON.NET .

Jako przykład rozważ następującą klasę C #:

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

Domyślnie, zwracając instancję tej klasy z kontrolera MVC jako JSON, zostanie ona serializowana w następujący sposób:

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

Chciałbym, aby został zserializowany (przez JSON.NET) jako:

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

Jak mam to zrobic?

aknuds1
źródło

Odpowiedzi:

389

lub po prostu:

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

Na przykład:

return new ContentResult
{
    ContentType = "application/json",
    Content = JsonConvert.SerializeObject(new { content = result, rows = dto }, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }),
    ContentEncoding = Encoding.UTF8
};
WebDever
źródło
2
Jest to jednak bardziej skomplikowane w użyciu, ponieważ należy skonfigurować ContentResult dla każdej metody kontrolera.
aknuds1
2
Tak, rozumiem, że twoja odpowiedź była rozwiązaniem wielokrotnego użytku, moim celem jest wyjaśnienie, że jest to tylko parametr metody Serialize.
WebDever,
1
Jeśli zwracasz JSON z Controllermetody, prawdopodobnie powinieneś użyć ApiController, w takim przypadku ta odpowiedź działa świetnie.
Simon Hartcher
1
@ SimonHartcher Rozważ jednak zakres pytania, a nie ogólny przypadek.
aknuds1
1
Prawidłowy typ zawartości dla JSON to application/jsonnie text/plain.
Fred
94

Znalazłem doskonałe rozwiązanie tego problemu na blogu Matsa Karlssona . Rozwiązaniem jest napisanie podklasy ActionResult, która serializuje dane za pośrednictwem JSON.NET, konfigurując tę ​​ostatnią zgodnie z konwencją 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));
    }
}

Następnie użyj tej klasy w następujący sposób w metodzie kontrolera MVC:

public ActionResult GetPerson()
{
    return new JsonCamelCaseResult(new Person { FirstName = "Joe", LastName = "Public" }, JsonRequestBehavior.AllowGet)};
}
aknuds1
źródło
3
Idealna odpowiedź: czysta i wielokrotnego użytku! Dziękuję Ci.
sander
1
Podczas gdy to rozwiązanie nadal działa. ale zasugerowano 4 lata temu. Czy mamy lepsze rozwiązanie?
SharpCoder,
59

W przypadku interfejsu WebAPI sprawdź ten link: http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx

Zasadniczo dodaj ten kod do Application_Start:

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Assaf S.
źródło
4
Web API i MVC zostały połączone w ASP.NET 6
AlexFoxGill
1
Łączenie dla wygody; ta konfiguracja gra naprawdę ładnie z tą odpowiedzią: stackoverflow.com/a/26068063/398630 (inne pytanie, ale używam ich razem, a ten link może uratować mnie i innych użytkowników googlowania w przyszłości).
BrainSlugs83
37

Myślę, że jest to prosta odpowiedź, której szukasz. Pochodzi z bloga Shawn Wildermuth :

// Add MVC services to the services container.
services.AddMvc()
  .AddJsonOptions(opts =>
  {
    opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  });
Quantium
źródło
2
Przepraszam chłopaki. Zbyt szybko przeczytałem ten post. To jest dla ASP.NET 5.
Quantium
8
Jak na ironię, przyszedłem tutaj, szukając odpowiedzi na pytanie, na które tutaj odpowiedziałeś, więc chociaż nie była to odpowiedź na pytanie PO, i tak mi pomogło. Dzięki! :)
porcus
1
Popieram to, co powiedział @porcus! Dzięki @Quantium!
Gromer
4
fyi Dla ASP.NET Core 1.0 jest to domyślnie skrzynka wielbłądów OOTB
Chris
3
Okazuje się, że nie jest to (dokładnie) domyślna wersja .NET Core 1.0. To rozwiązanie wpływa na właściwości dynamiczne i domyślnie nie mają na nie wpływu. stackoverflow.com/questions/41329279/…
Niels Brinch
13

Alternatywą dla niestandardowego filtra jest utworzenie metody rozszerzenia do serializacji dowolnego obiektu do 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);
    }
}

Następnie wywołaj go po powrocie z akcji kontrolera.

return Content(person.ToJson(), "application/json");
Insygnia Stuarta
źródło
Elegancki i prosty.
markau
1
Możesz nawet przenieść ustawienia do statycznego pola tylko do odczytu i dodać metodę uzupełniania FromJson.
Vapor in the Alley
8

Prostsze jest lepsze IMO!

Dlaczego tego nie robisz?

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

Prosty kontroler klasy bazowej

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

W ASP.NET Core MVC.

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

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

        return Json(data, settings);
    }
Fred
źródło
Co więcej, umieść go w pliku Startup.cs.
FatAlbert
6

Poniżej znajduje się metoda akcji, która zwraca ciąg json (cameCase) poprzez serializację tablicy obiektów.

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

Uwaga: instancja JsonSerializerSettings przekazana jako drugi parametr. Właśnie dlatego tak się stało.

DanKodi
źródło
4

Podobało mi się to:

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

ta prosta metoda rozszerzenia w rdzeniu MVC, da ToJson () możliwość każdemu obiektowi w twoim projekcie. Moim zdaniem w projekcie MVC większość obiektów powinna mieć możliwość zostania jsonem, oczywiście to zależy :)

Ali Alp
źródło
Rozważ wyodrębnienie zmiennej „settings” poza metodą (jako prywatne pole statyczne „camelCaseSettings”), aby nie inicjować nowej zmiennej za każdym razem, gdy wywoływana jest metoda ToJson.
Ekus
4

Musisz ustawić ustawienia w pliku „Startup.cs”

Musisz także zdefiniować go w domyślnych wartościach JsonConvert, to jest, jeśli później chcesz bezpośrednio użyć biblioteki do serializacji obiektu.

    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
źródło
Zauważ, że ta odpowiedź jest poprawna dla ASP.NET Core, ale nie ASP.NET (która jest ramą w pytaniu).
Nate Barbettini,
0

Jeśli zwracasz ActionResult w interfejsie API .NET Core lub wynik IHttpAction, możesz po prostu zawinąć swój model w metodę Ok (), która dopasuje obudowę do twojego interfejsu i serializuje go dla Ciebie. Nie musisz używać JsonConvert. :)

LukePerrin
źródło