diff --git a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs index eb6b2cb262..77be650884 100644 --- a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs +++ b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs @@ -40,7 +40,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters if (jsonSerializerOptions.Encoder is null) { // If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters. - jsonSerializerOptions = jsonSerializerOptions.Copy(JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + jsonSerializerOptions = new JsonSerializerOptions(jsonSerializerOptions) + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + }; } return new SystemTextJsonOutputFormatter(jsonSerializerOptions); diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/JsonSerializerOptionsCopyConstructor.cs b/src/Mvc/Mvc.Core/src/Infrastructure/JsonSerializerOptionsCopyConstructor.cs deleted file mode 100644 index f378aec55e..0000000000 --- a/src/Mvc/Mvc.Core/src/Infrastructure/JsonSerializerOptionsCopyConstructor.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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.Text.Encodings.Web; - -namespace System.Text.Json -{ - internal static class JsonSerializerOptionsCopyConstructor - { - public static JsonSerializerOptions Copy(this JsonSerializerOptions serializerOptions, JavaScriptEncoder encoder) - { - var copiedOptions = new JsonSerializerOptions - { - AllowTrailingCommas = serializerOptions.AllowTrailingCommas, - DefaultBufferSize = serializerOptions.DefaultBufferSize, - DictionaryKeyPolicy = serializerOptions.DictionaryKeyPolicy, - IgnoreNullValues = serializerOptions.IgnoreNullValues, - IgnoreReadOnlyProperties = serializerOptions.IgnoreReadOnlyProperties, - MaxDepth = serializerOptions.MaxDepth, - PropertyNameCaseInsensitive = serializerOptions.PropertyNameCaseInsensitive, - PropertyNamingPolicy = serializerOptions.PropertyNamingPolicy, - ReadCommentHandling = serializerOptions.ReadCommentHandling, - WriteIndented = serializerOptions.WriteIndented - }; - - for (var i = 0; i < serializerOptions.Converters.Count; i++) - { - copiedOptions.Converters.Add(serializerOptions.Converters[i]); - } - - copiedOptions.Encoder = encoder; - - return copiedOptions; - } - } -} diff --git a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs index cd245872ef..ca9180f971 100644 --- a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs +++ b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs @@ -1,6 +1,14 @@ // 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.IO; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using Xunit; + namespace Microsoft.AspNetCore.Mvc.Formatters { public class SystemTextJsonOutputFormatterTest : JsonOutputFormatterTestBase @@ -9,5 +17,51 @@ namespace Microsoft.AspNetCore.Mvc.Formatters { return SystemTextJsonOutputFormatter.CreateFormatter(new JsonOptions()); } + + [Fact] + public async Task WriteResponseBodyAsync_AllowsConfiguringPreserveReferenceHandling() + { + // Arrange + var formatter = GetOutputFormatter(); + ((SystemTextJsonOutputFormatter)formatter).SerializerOptions.ReferenceHandling = ReferenceHandling.Preserve; + var expectedContent = "{\"$id\":\"1\",\"name\":\"Person\",\"child\":{\"$id\":\"2\",\"name\":\"Child\",\"child\":null,\"parent\":{\"$ref\":\"1\"}},\"parent\":null}"; + var person = new Person + { + Name = "Person", + Child = new Person { Name = "Child", }, + }; + person.Child.Parent = person; + + var mediaType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + var encoding = CreateOrGetSupportedEncoding(formatter, "utf-8", isDefaultEncoding: true); + + var body = new MemoryStream(); + var actionContext = GetActionContext(mediaType, body); + + var outputFormatterContext = new OutputFormatterWriteContext( + actionContext.HttpContext, + new TestHttpResponseStreamWriterFactory().CreateWriter, + typeof(Person), + person) + { + ContentType = new StringSegment(mediaType.ToString()), + }; + + // Act + await formatter.WriteResponseBodyAsync(outputFormatterContext, Encoding.GetEncoding("utf-8")); + + // Assert + var actualContent = encoding.GetString(body.ToArray()); + Assert.Equal(expectedContent, actualContent); + } + + private class Person + { + public string Name { get; set; } + + public Person Child { get; set; } + + public Person Parent { get; set; } + } } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs b/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs index 48f74b2113..c2c3730b65 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs @@ -33,7 +33,10 @@ namespace Microsoft.AspNetCore.Mvc.Rendering return serializerOptions; } - return serializerOptions.Copy(JavaScriptEncoder.Default); + return new JsonSerializerOptions(serializerOptions) + { + Encoder = JavaScriptEncoder.Default, + }; } } }