From 62803d5979b13e3dcd4929a04274a40cc242a449 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Wed, 8 Jun 2016 15:10:26 -0700 Subject: [PATCH] Serialize JSON using camel case by default - #4283 - update `JsonSerializerSettingsProvider` to choose the `CamelCaseNamingStrategy` --- .../JsonSerializerSettingsProvider.cs | 8 +++- .../BasicTests.cs | 45 ++++++++++++------- .../ContentNegotiationTest.cs | 8 ++-- .../JsonOutputFormatterTests.cs | 3 +- .../JsonResultTest.cs | 6 +-- .../RespectBrowserAcceptHeaderTests.cs | 4 +- .../WebApiCompatShimActionResultTest.cs | 20 ++++----- .../WebApiCompatShimParameterBindingTest.cs | 6 +-- .../Controllers/HomeController.cs | 13 +++--- test/WebSites/BasicWebSite/Models/Person.cs | 4 +- .../Home/JsonHelperWithSettingsInView.cshtml | 5 ++- 11 files changed, 72 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs index cf26ca32d4..8dc4bf2d5c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace Microsoft.AspNetCore.Mvc.Formatters { @@ -20,6 +21,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters { return new JsonSerializerSettings { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy(), + }, + MissingMemberHandling = MissingMemberHandling.Ignore, // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions @@ -28,7 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters // Do not change this setting // Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types - TypeNameHandling = TypeNameHandling.None + TypeNameHandling = TypeNameHandling.None, }; } } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs index a3a558be35..010ba08631 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs @@ -8,6 +8,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Formatters; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Xunit; @@ -217,15 +218,10 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests } [Fact] - public async Task JsonHelper_RendersJson() + public async Task JsonHelper_RendersJson_WithCamelCaseNames() { // Arrange - var json = JsonConvert.SerializeObject(new BasicWebSite.Models.Person() - { - Id = 9000, - Name = "John Smith" - }); - + var json = "{\"id\":9000,\"fullName\":\"John Smith\"}"; var expectedBody = string.Format( @"", + json); + + // Act + var response = await Client.GetAsync("Home/JsonHelperWithSettingsInView?snakeCase=true"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ContentNegotiationTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ContentNegotiationTest.cs index 81306a53b8..c94f6047de 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ContentNegotiationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ContentNegotiationTest.cs @@ -44,8 +44,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests { // Arrange var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8"); - var expectedBody = $"{{{Environment.NewLine} \"Name\": \"My name\",{Environment.NewLine}" + - $" \"Address\": \"My address\"{Environment.NewLine}}}"; + var expectedBody = $"{{{Environment.NewLine} \"name\": \"My name\",{Environment.NewLine}" + + $" \"address\": \"My address\"{Environment.NewLine}}}"; // Act var response = await Client.GetAsync("http://localhost/Normal/MultipleAllowedContentTypes"); @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests { // Arrange var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8"); - var expectedOutput = "{\"Name\":\"John\",\"Address\":\"One Microsoft Way\"}"; + var expectedOutput = "{\"name\":\"John\",\"address\":\"One Microsoft Way\"}"; var request = new HttpRequestMessage( HttpMethod.Get, "http://localhost/ContentNegotiation/UserInfo_ProducesWithTypeOnly"); @@ -287,7 +287,7 @@ END:VCARD { // Arrange var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8"); - var expectedBody = "{\"MethodName\":\"Produces_WithNonObjectResult\"}"; + var expectedBody = "{\"methodName\":\"Produces_WithNonObjectResult\"}"; // Act var response = await Client.GetAsync("http://localhost/ProducesJson/Produces_WithNonObjectResult"); diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonOutputFormatterTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonOutputFormatterTests.cs index 2d17bd5365..c8e8b2cd59 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonOutputFormatterTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonOutputFormatterTests.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Testing.xunit; using Newtonsoft.Json; using Xunit; @@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Name = "John Williams" }; - var serializerSettings = new JsonSerializerSettings(); + var serializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings(); serializerSettings.Formatting = Formatting.Indented; var expectedBody = JsonConvert.SerializeObject(user, serializerSettings); diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonResultTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonResultTest.cs index cf56bb1e45..ebf4e8846f 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonResultTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/JsonResultTest.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); - Assert.Equal("{\"Message\":\"hello\"}", content); + Assert.Equal("{\"message\":\"hello\"}", content); } // Using an Accept header can't force Json to not be Json. If your accept header doesn't jive with the @@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); - Assert.Equal("{\"Message\":\"hello\"}", content); + Assert.Equal("{\"message\":\"hello\"}", content); } // If the object is null, it will get formatted as JSON. NOT as a 204/NoContent @@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/message+json", response.Content.Headers.ContentType.MediaType); - Assert.Equal("{\"Message\":\"hello\"}", content); + Assert.Equal("{\"message\":\"hello\"}", content); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RespectBrowserAcceptHeaderTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RespectBrowserAcceptHeaderTests.cs index 147b131524..6080d661d4 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RespectBrowserAcceptHeaderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RespectBrowserAcceptHeaderTests.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.NotNull(response.Content.Headers.ContentType); Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); var responseData = await response.Content.ReadAsStringAsync(); - Assert.Equal("{\"Id\":10,\"Name\":\"John\"}", responseData); + Assert.Equal("{\"id\":10,\"name\":\"John\"}", responseData); } [ConditionalTheory] @@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests "35Jimmy" + ""; - var expectedResponseData = @"{""Id"":35,""Name"":""Jimmy""}"; + var expectedResponseData = @"{""id"":35,""name"":""Jimmy""}"; var request = RequestWithAccept("http://localhost/RespectBrowserAcceptHeader/CreateEmployee", acceptHeader); request.Content = new StringContent(requestData, Encoding.UTF8, "application/xml"); request.Method = HttpMethod.Post; diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs index d0f6f46a15..5df40f3129 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs @@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Ambiguous, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); } [Fact] @@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); Assert.Equal("5", response.Headers.Location.OriginalString); } @@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); Assert.Equal("/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); } @@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); Assert.Equal("http://localhost/api/Blog/ActionResult/5", response.Headers.Location.OriginalString); } @@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); Assert.Equal("/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); } @@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); Assert.Equal("http://localhost/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); } @@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); } [Fact] @@ -182,7 +182,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Arrange var expected = "{" + Environment.NewLine + - " \"Name\": \"Test User\"" + Environment.NewLine + + " \"name\": \"Test User\"" + Environment.NewLine + "}"; // Act @@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Arrange var expected = "{" + Environment.NewLine + - " \"Name\": \"Test User\"" + Environment.NewLine + + " \"name\": \"Test User\"" + Environment.NewLine + "}"; // Act @@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("{\"name\":\"Test User\"}", content); } [Fact] diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimParameterBindingTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimParameterBindingTest.cs index 0b9f1e0004..0828bc8cf6 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimParameterBindingTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/WebApiCompatShimParameterBindingTest.cs @@ -150,7 +150,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("{\"Id\":5,\"Name\":\"Test Employee\"}", content); + Assert.Equal("{\"id\":5,\"name\":\"Test Employee\"}", content); } [Fact] @@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("{\"Id\":5,\"Name\":\"Test Employee\"}", content); + Assert.Equal("{\"id\":5,\"name\":\"Test Employee\"}", content); } // name is read from the url - and the rest from the body (formatters) @@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("{\"Id\":5,\"Name\":\"Name_Override\"}", content); + Assert.Equal("{\"id\":5,\"name\":\"Name_Override\"}", content); } } } diff --git a/test/WebSites/BasicWebSite/Controllers/HomeController.cs b/test/WebSites/BasicWebSite/Controllers/HomeController.cs index ad65b93771..fa2a854aae 100644 --- a/test/WebSites/BasicWebSite/Controllers/HomeController.cs +++ b/test/WebSites/BasicWebSite/Controllers/HomeController.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using BasicWebSite.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; +using Newtonsoft.Json.Serialization; namespace BasicWebSite.Controllers { @@ -65,20 +65,21 @@ namespace BasicWebSite.Controllers { Person person = new Person { - Id = 9000, - Name = "John Smith" + id = 9000, + FullName = "John Smith" }; return View(person); } - public IActionResult JsonHelperWithSettingsInView() + public IActionResult JsonHelperWithSettingsInView(bool snakeCase) { Person person = new Person { - Id = 9000, - Name = "John Smith" + id = 9000, + FullName = "John Smith" }; + ViewData["naming"] = snakeCase ? (NamingStrategy)new SnakeCaseNamingStrategy() : new DefaultNamingStrategy(); return View(person); } diff --git a/test/WebSites/BasicWebSite/Models/Person.cs b/test/WebSites/BasicWebSite/Models/Person.cs index 510aef31cd..7fec349f34 100644 --- a/test/WebSites/BasicWebSite/Models/Person.cs +++ b/test/WebSites/BasicWebSite/Models/Person.cs @@ -5,8 +5,8 @@ namespace BasicWebSite.Models { public class Person { - public int Id { get; set; } + public int id { get; set; } - public string Name { get; set; } + public string FullName { get; set; } } } \ No newline at end of file diff --git a/test/WebSites/BasicWebSite/Views/Home/JsonHelperWithSettingsInView.cshtml b/test/WebSites/BasicWebSite/Views/Home/JsonHelperWithSettingsInView.cshtml index 7ccd19110c..1f551bc9d3 100644 --- a/test/WebSites/BasicWebSite/Views/Home/JsonHelperWithSettingsInView.cshtml +++ b/test/WebSites/BasicWebSite/Views/Home/JsonHelperWithSettingsInView.cshtml @@ -3,7 +3,10 @@ @using Newtonsoft.Json.Serialization @{ var settings = JsonSerializerSettingsProvider.CreateSerializerSettings(); - settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + settings.ContractResolver = new DefaultContractResolver + { + NamingStrategy = (NamingStrategy)(ViewData["naming"]), + }; }