diff --git a/src/Mvc/Mvc.Core/src/MvcOptions.cs b/src/Mvc/Mvc.Core/src/MvcOptions.cs
index 52da78e8e2..3fa005f96c 100644
--- a/src/Mvc/Mvc.Core/src/MvcOptions.cs
+++ b/src/Mvc/Mvc.Core/src/MvcOptions.cs
@@ -329,7 +329,16 @@ namespace Microsoft.AspNetCore.Mvc
/// Gets the used by and
/// .
///
- public JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions();
+ public JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions
+ {
+ // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions
+ // from deserialization errors that might occur from deeply nested objects.
+ // This value is the same for model binding and Json.Net's serialization.
+ MaxDepth = DefaultMaxModelBindingRecursionDepth,
+
+ // Use camel casing for properties
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ };
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
diff --git a/src/Mvc/Mvc.Core/test/Formatters/JsonInputFormatterTestBase.cs b/src/Mvc/Mvc.Core/test/Formatters/JsonInputFormatterTestBase.cs
index fe7024b089..e7190037e9 100644
--- a/src/Mvc/Mvc.Core/test/Formatters/JsonInputFormatterTestBase.cs
+++ b/src/Mvc/Mvc.Core/test/Formatters/JsonInputFormatterTestBase.cs
@@ -106,7 +106,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
public virtual async Task JsonFormatterReadsDateTimeValue()
{
// Arrange
- var content = "\"2012-02-01 12:45 AM\"";
+ var expected = new DateTime(2012, 02, 01, 00, 45, 00);
+ var content = $"\"{expected.ToString("O")}\"";
var formatter = GetInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(content);
@@ -120,7 +121,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// Assert
Assert.False(result.HasError);
var dateValue = Assert.IsType(result.Model);
- Assert.Equal(new DateTime(2012, 02, 01, 00, 45, 00), dateValue);
+ Assert.Equal(expected, dateValue);
}
[Fact]
@@ -129,7 +130,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// Arrange
var formatter = GetInputFormatter();
- var content = "{\"Name\": \"Person Name\", \"Age\": 30}";
+ var content = "{\"name\": \"Person Name\", \"age\": 30}";
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetHttpContext(contentBytes);
@@ -194,8 +195,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// Assert
Assert.False(result.HasError);
- var integers = Assert.IsType>(result.Model);
- Assert.Equal(new int[] { 0, 23, 300 }, integers);
+ Assert.IsAssignableFrom(requestedType, result.Model);
+ Assert.Equal(new int[] { 0, 23, 300 }, (IEnumerable)result.Model);
}
[Fact]
diff --git a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
index 72ac638842..020c357961 100644
--- a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
+++ b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
@@ -8,12 +8,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
public class SystemTextJsonInputFormatterTest : JsonInputFormatterTestBase
{
- [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8489")]
- public override Task JsonFormatterReadsDateTimeValue()
- {
- return base.JsonFormatterReadsDateTimeValue();
- }
-
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
public override Task ReadAsync_AddsModelValidationErrorsToModelState()
{
@@ -38,24 +32,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
return base.ReadAsync_UsesTryAddModelValidationErrorsToModelState();
}
- [Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
- public override Task ReadAsync_ReadsValidArray_AsCollectionOfT()
- {
- return base.ReadAsync_ReadsValidArray_AsCollectionOfT();
- }
-
- [Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
- public override Task ReadAsync_ReadsValidArray_AsEnumerableOfT()
- {
- return base.ReadAsync_ReadsValidArray_AsEnumerableOfT();
- }
-
- [Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
- public override Task ReadAsync_ReadsValidArray_AsIListOfT()
- {
- return base.ReadAsync_ReadsValidArray_AsIListOfT();
- }
-
protected override TextInputFormatter GetInputFormatter()
{
return new SystemTextJsonInputFormatter(new MvcOptions());
diff --git a/src/Mvc/Mvc.NewtonsoftJson/test/BsonTempDataSerializerTest.cs b/src/Mvc/Mvc.NewtonsoftJson/test/BsonTempDataSerializerTest.cs
index 2ef1519774..a2438b1b8b 100644
--- a/src/Mvc/Mvc.NewtonsoftJson/test/BsonTempDataSerializerTest.cs
+++ b/src/Mvc/Mvc.NewtonsoftJson/test/BsonTempDataSerializerTest.cs
@@ -213,6 +213,28 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
testProvider.EnsureObjectCanBeSerialized(value);
}
+ [Fact]
+ public override void RoundTripTest_GuidToString()
+ {
+ // Documents the behavior of round-tripping a Guid value as a string
+ // Arrange
+ var key = "test-key";
+ var testProvider = GetTempDataSerializer();
+ var value = Guid.NewGuid();
+ var input = new Dictionary
+ {
+ { key, value.ToString() }
+ };
+
+ // Act
+ var bytes = testProvider.Serialize(input);
+ var values = testProvider.Deserialize(bytes);
+
+ // Assert
+ var roundTripValue = Assert.IsType(values[key]);
+ Assert.Equal(value.ToString(), roundTripValue);
+ }
+
private class TestItem
{
public int DummyInt { get; set; }
diff --git a/src/Mvc/Mvc.ViewFeatures/src/Infrastructure/DefaultTempDataSerializer.cs b/src/Mvc/Mvc.ViewFeatures/src/Infrastructure/DefaultTempDataSerializer.cs
index 026f4d0528..27b30c9dc6 100644
--- a/src/Mvc/Mvc.ViewFeatures/src/Infrastructure/DefaultTempDataSerializer.cs
+++ b/src/Mvc/Mvc.ViewFeatures/src/Infrastructure/DefaultTempDataSerializer.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Text.Json;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
@@ -46,19 +45,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
break;
case JsonValueType.String:
- var stringValue = item.Value.GetString();
- // BsonTempDataSerializer will parse certain types of string values. We'll attempt to imitiate it.
- if (DateTime.TryParseExact(stringValue, "r", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
- {
- deserializedValue = dateTime;
- }
- else if (Guid.TryParseExact(stringValue, "B", out var guid))
+ if (item.Value.TryGetGuid(out var guid))
{
deserializedValue = guid;
}
+ else if (item.Value.TryGetDateTime(out var dateTime))
+ {
+ deserializedValue = dateTime;
+ }
else
{
- deserializedValue = stringValue;
+ deserializedValue = item.Value.GetString();
}
break;
@@ -123,11 +120,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
break;
case DateTime dateTime:
- writer.WriteString(key, dateTime.ToString("r", CultureInfo.InvariantCulture));
+ writer.WriteString(key, dateTime);
break;
case Guid guid:
- writer.WriteString(key, guid.ToString("B", CultureInfo.InvariantCulture));
+ writer.WriteString(key, guid);
break;
}
}
diff --git a/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/DefaultTempDataSerializerTest.cs b/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/DefaultTempDataSerializerTest.cs
index 18f0f2f9cb..982e9fc9f1 100644
--- a/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/DefaultTempDataSerializerTest.cs
+++ b/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/DefaultTempDataSerializerTest.cs
@@ -12,11 +12,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
protected override TempDataSerializer GetTempDataSerializer() => new DefaultTempDataSerializer();
[Fact]
- public void RoundTripTest_StringThatLooksLikeCompliantDateTime()
+ public void RoundTripTest_NonStandardDateTimeStringFormat_RoundTripsAsString()
{
- // This is an unintentional side-effect of trying to support a compat with JSON.NET.
- // Any string that looks like a compliant DateTime object will be parsed as a DateTime.
- // This test documents this behavior.
+ // DateTime that do not match the format that System.Text.Json uses for round-tripping
+ // should round-trip as strings.
+
// Arrange
var key = "test-key";
var testProvider = GetTempDataSerializer();
@@ -30,57 +30,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
var bytes = testProvider.Serialize(input);
var values = testProvider.Deserialize(bytes);
- // Assert
- var roundTripValue = Assert.IsType(values[key]);
- Assert.Equal(value, roundTripValue);
- }
-
- [Fact]
- public void RoundTripTest_StringThatIsNotCompliantDateTime()
- {
- // This is an unintentional side-effect of trying to support a compat with JSON.NET.
- // Any string that looks like a compliant DateTime object will be parsed as a DateTime.
- // This test documents this behavior.
- // Arrange
- var key = "test-key";
- var testProvider = GetTempDataSerializer();
- var value = new DateTime(2009, 1, 1, 12, 37, 43);
- var input = new Dictionary
- {
- { key, value.ToString() }
- };
-
- // Act
- var bytes = testProvider.Serialize(input);
- var values = testProvider.Deserialize(bytes);
-
// Assert
var roundTripValue = Assert.IsType(values[key]);
- Assert.Equal(value.ToString(), roundTripValue);
- }
-
- [Fact]
- public void RoundTripTest_StringThatIsNotCompliantGuid()
- {
- // This is an unintentional side-effect of trying to support a compat with JSON.NET.
- // Any string that looks like a compliant DateTime object will be parsed as a DateTime.
- // This test documents this behavior.
- // Arrange
- var key = "test-key";
- var testProvider = GetTempDataSerializer();
- var value = Guid.NewGuid();
- var input = new Dictionary
- {
- { key, value.ToString() }
- };
-
- // Act
- var bytes = testProvider.Serialize(input);
- var values = testProvider.Deserialize(bytes);
-
- // Assert
- var roundTripValue = Assert.IsType(values[key]);
- Assert.Equal(value.ToString(), roundTripValue);
+ Assert.Equal(value.ToString("r"), roundTripValue);
}
}
}
diff --git a/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/TempDataSerializerTestBase.cs b/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/TempDataSerializerTestBase.cs
index b4030d9bea..38a540f8e7 100644
--- a/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/TempDataSerializerTestBase.cs
+++ b/src/Mvc/Mvc.ViewFeatures/test/Infrastructure/TempDataSerializerTestBase.cs
@@ -173,6 +173,72 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
Assert.Equal(value, roundTripValue);
}
+ [Fact]
+ public virtual void RoundTripTest_DateTimeToString()
+ {
+ // Documents the behavior of round-tripping a DateTime value as a string
+ // Arrange
+ var key = "test-key";
+ var testProvider = GetTempDataSerializer();
+ var value = new DateTime(2009, 1, 1, 12, 37, 43);
+ var input = new Dictionary
+ {
+ { key, value.ToString() }
+ };
+
+ // Act
+ var bytes = testProvider.Serialize(input);
+ var values = testProvider.Deserialize(bytes);
+
+ // Assert
+ var roundTripValue = Assert.IsType(values[key]);
+ Assert.Equal(value.ToString(), roundTripValue);
+ }
+
+ [Fact]
+ public virtual void RoundTripTest_StringThatIsNotCompliantGuid()
+ {
+ // Documents the behavior of round-tripping a Guid with a non-default format specifier
+ // Arrange
+ var key = "test-key";
+ var testProvider = GetTempDataSerializer();
+ var value = Guid.NewGuid();
+ var input = new Dictionary
+ {
+ { key, value.ToString("N") }
+ };
+
+ // Act
+ var bytes = testProvider.Serialize(input);
+ var values = testProvider.Deserialize(bytes);
+
+ // Assert
+ var roundTripValue = Assert.IsType(values[key]);
+ Assert.Equal(value.ToString("N"), roundTripValue);
+ }
+
+ [Fact]
+ public virtual void RoundTripTest_GuidToString()
+ {
+ // Documents the behavior of round-tripping a Guid value as a string
+ // Arrange
+ var key = "test-key";
+ var testProvider = GetTempDataSerializer();
+ var value = Guid.NewGuid();
+ var input = new Dictionary
+ {
+ { key, value.ToString() }
+ };
+
+ // Act
+ var bytes = testProvider.Serialize(input);
+ var values = testProvider.Deserialize(bytes);
+
+ // Assert
+ var roundTripValue = Assert.IsType(values[key]);
+ Assert.Equal(value, roundTripValue);
+ }
+
protected abstract TempDataSerializer GetTempDataSerializer();
}
}
diff --git a/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs b/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs
index de4a5dee17..75ac9df6d9 100644
--- a/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs
+++ b/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs
@@ -507,7 +507,7 @@ Products: Book1, Book2 (1)";
// Act - 3
// Trigger an expiration of the nested content.
- var content = @"[{ ""ProductName"": ""Music Systems"" },{ ""ProductName"": ""Televisions"" }]";
+ var content = @"[{ ""productName"": ""Music Systems"" },{ ""productName"": ""Televisions"" }]";
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/categories/Electronics");
requestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
(await Client.SendAsync(requestMessage)).EnsureSuccessStatusCode();
diff --git a/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs b/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs
index a8940bceb6..ceeb88fd48 100644
--- a/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs
+++ b/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs
@@ -1,10 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
@@ -31,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
// Arrange
var sampleInputInt = 10;
- var input = "{\"SampleInt\":10}";
+ var input = "{\"sampleInt\":10}";
var content = new StringContent(input, Encoding.UTF8, requestContentType);
// Act
@@ -43,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Theory]
- [InlineData("application/json", "{\"SampleInt\":10}", 10)]
+ [InlineData("application/json", "{\"sampleInt\":10}", 10)]
[InlineData("application/json", "{}", 0)]
public async Task JsonInputFormatter_IsModelStateValid_ForValidContentType(
string requestContentType,
@@ -100,7 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public async Task JsonInputFormatter_Returns415UnsupportedMediaType_ForEmptyContentType()
{
// Arrange
- var jsonInput = "{\"SampleInt\":10}";
+ var jsonInput = "{\"sampleInt\":10}";
var content = new StringContent(jsonInput, Encoding.UTF8, "application/json");
content.Headers.Clear();
@@ -112,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Theory]
- [InlineData("application/json", "{\"SampleInt\":10}", 10)]
+ [InlineData("application/json", "{\"sampleInt\":10}", 10)]
[InlineData("application/json", "{}", 0)]
public async Task JsonInputFormatter_IsModelStateValid_ForTransferEncodingChunk(
string requestContentType,
@@ -133,6 +135,5 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedSampleIntValue.ToString(), responseBody);
}
-
}
}
\ No newline at end of file
diff --git a/src/Mvc/test/Mvc.FunctionalTests/SystemTextJsonOutputFormatterTest.cs b/src/Mvc/test/Mvc.FunctionalTests/SystemTextJsonOutputFormatterTest.cs
index 321c4b1de1..15af231bea 100644
--- a/src/Mvc/test/Mvc.FunctionalTests/SystemTextJsonOutputFormatterTest.cs
+++ b/src/Mvc/test/Mvc.FunctionalTests/SystemTextJsonOutputFormatterTest.cs
@@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
}
- [Fact(Skip = "Insert issue here")]
+ [Fact(Skip = "Dictionary serialization does not correctly work.")]
public override Task SerializableErrorIsReturnedInExpectedFormat() => base.SerializableErrorIsReturnedInExpectedFormat();
[Fact]
@@ -29,67 +29,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("\"Hello Mr. \\ud83e\\udd8a\"", await response.Content.ReadAsStringAsync());
}
- [Fact]
- public override async Task Formatting_SimpleModel()
- {
- // Arrange
- var expected = "{\"Id\":10,\"Name\":\"Test\",\"StreetName\":\"Some street\"}";
-
- // Act
- var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.SimpleModelResult)}");
-
- // Assert
- await response.AssertStatusCodeAsync(HttpStatusCode.OK);
- Assert.Equal(expected, await response.Content.ReadAsStringAsync());
- }
-
- [Fact]
- public override async Task Formatting_CollectionType()
- {
- // Arrange
- var expected = "[{\"Id\":10,\"Name\":\"TestName\",\"StreetName\":null},{\"Id\":11,\"Name\":\"TestName1\",\"StreetName\":\"Some street\"}]";
-
- // Act
- var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.CollectionModelResult)}");
-
- // Assert
- await response.AssertStatusCodeAsync(HttpStatusCode.OK);
- Assert.Equal(expected, await response.Content.ReadAsStringAsync());
- }
-
[Fact(Skip = "Dictionary serialization does not correctly work.")]
public override Task Formatting_DictionaryType() => base.Formatting_DictionaryType();
[Fact(Skip = "Dictionary serialization does not correctly work.")]
public override Task Formatting_ProblemDetails() => base.Formatting_ProblemDetails();
- [Fact]
- public override async Task Formatting_PolymorphicModel()
- {
- // Arrange
- var expected = "{\"Id\":10,\"Name\":\"test\",\"StreetName\":null}";
-
- // Act
- var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.PolymorphicResult)}");
-
- // Assert
- await response.AssertStatusCodeAsync(HttpStatusCode.OK);
- Assert.Equal(expected, await response.Content.ReadAsStringAsync());
- }
-
- [Fact]
- public override async Task Formatting_LargeObject()
- {
- // Arrange
- var expectedName = "This is long so we can test large objects " + new string('a', 1024 * 65);
- var expected = $"{{\"Id\":10,\"Name\":\"{expectedName}\",\"StreetName\":null}}";
-
- // Act
- var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.LargeObjectResult)}");
-
- // Assert
- await response.AssertStatusCodeAsync(HttpStatusCode.OK);
- Assert.Equal(expected, await response.Content.ReadAsStringAsync());
- }
+ [Fact(Skip = "https://github.com/dotnet/corefx/issues/36166")]
+ public override Task Formatting_PolymorphicModel() => base.Formatting_PolymorphicModel();
}
}
\ No newline at end of file