Serialize JSON using camel case by default

- #4283
- update `JsonSerializerSettingsProvider` to choose the `CamelCaseNamingStrategy`
This commit is contained in:
Doug Bunting 2016-06-08 15:10:26 -07:00
parent 31ec88526a
commit 62803d5979
11 changed files with 72 additions and 50 deletions

View File

@ -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,
};
}
}

View File

@ -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 <b>Smith</b>"
});
var json = "{\"id\":9000,\"fullName\":\"John <b>Smith</b>\"}";
var expectedBody = string.Format(
@"<script type=""text/javascript"">
var json = {0};
@ -244,17 +240,10 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Fact]
public async Task JsonHelperWithSettings_RendersJson()
public async Task JsonHelperWithSettings_RendersJson_WithNamesUnchanged()
{
// Arrange
var json = JsonConvert.SerializeObject(
new BasicWebSite.Models.Person()
{
Id = 9000,
Name = "John <b>Smith</b>"
},
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
var json = "{\"id\":9000,\"FullName\":\"John <b>Smith</b>\"}";
var expectedBody = string.Format(
@"<script type=""text/javascript"">
var json = {0};
@ -262,7 +251,29 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
json);
// Act
var response = await Client.GetAsync("Home/JsonHelperWithSettingsInView");
var response = await Client.GetAsync("Home/JsonHelperWithSettingsInView?snakeCase=false");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/html", response.Content.Headers.ContentType.MediaType);
var actualBody = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, actualBody, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task JsonHelperWithSettings_RendersJson_WithSnakeCaseNames()
{
// Arrange
var json = "{\"id\":9000,\"full_name\":\"John <b>Smith</b>\"}";
var expectedBody = string.Format(
@"<script type=""text/javascript"">
var json = {0};
</script>",
json);
// Act
var response = await Client.GetAsync("Home/JsonHelperWithSettingsInView?snakeCase=true");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -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");

View File

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

View File

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

View File

@ -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
"<RespectBrowserAcceptHeaderController.Employee xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.datacontract.org/2004/07/FormatterWebSite.Controllers\"><Id>35</Id><Name>Jimmy" +
"</Name></RespectBrowserAcceptHeaderController.Employee>";
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;

View File

@ -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]

View File

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

View File

@ -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 <b>Smith</b>"
id = 9000,
FullName = "John <b>Smith</b>"
};
return View(person);
}
public IActionResult JsonHelperWithSettingsInView()
public IActionResult JsonHelperWithSettingsInView(bool snakeCase)
{
Person person = new Person
{
Id = 9000,
Name = "John <b>Smith</b>"
id = 9000,
FullName = "John <b>Smith</b>"
};
ViewData["naming"] = snakeCase ? (NamingStrategy)new SnakeCaseNamingStrategy() : new DefaultNamingStrategy();
return View(person);
}

View File

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

View File

@ -3,7 +3,10 @@
@using Newtonsoft.Json.Serialization
@{
var settings = JsonSerializerSettingsProvider.CreateSerializerSettings();
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = (NamingStrategy)(ViewData["naming"]),
};
}
<script type="text/javascript">
var json = @Json.Serialize(Model, settings);