Skip to content

Commit 8d09403

Browse files
author
Pranav Krishnamoorthy
committed
Merged PR 7268: Avoid caching JsonSerializer
Avoid caching JsonSerializer
1 parent 1aa1069 commit 8d09403

File tree

4 files changed

+79
-8
lines changed

4 files changed

+79
-8
lines changed

eng/PatchConfig.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Later on, this will be checked using this condition:
6262
</PropertyGroup>
6363
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.1.18' ">
6464
<PackagesInPatch>
65+
Microsoft.AspNetCore.Mvc.Formatters.Json;
6566
</PackagesInPatch>
6667
</PropertyGroup>
6768
</Project>

src/Mvc/Mvc.Formatters.Json/src/JsonOutputFormatter.cs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
1818
public class JsonOutputFormatter : TextOutputFormatter
1919
{
2020
private readonly IArrayPool<char> _charPool;
21-
22-
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
23-
// the serializer and invalidate it when the settings change.
24-
private JsonSerializer _serializer;
21+
private JsonSerializerSettings _serializerSettings;
2522

2623
/// <summary>
2724
/// Initializes a new <see cref="JsonOutputFormatter"/> instance.
@@ -121,12 +118,12 @@ protected virtual JsonWriter CreateJsonWriter(TextWriter writer)
121118
/// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns>
122119
protected virtual JsonSerializer CreateJsonSerializer()
123120
{
124-
if (_serializer == null)
121+
if (_serializerSettings == null)
125122
{
126-
_serializer = JsonSerializer.Create(SerializerSettings);
123+
_serializerSettings = ShallowCopy(SerializerSettings);
127124
}
128125

129-
return _serializer;
126+
return JsonSerializer.Create(_serializerSettings);
130127
}
131128

132129
/// <inheritdoc />
@@ -153,5 +150,43 @@ public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext co
153150
await writer.FlushAsync();
154151
}
155152
}
153+
154+
private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
155+
{
156+
var copiedSettings = new JsonSerializerSettings
157+
{
158+
FloatParseHandling = settings.FloatParseHandling,
159+
FloatFormatHandling = settings.FloatFormatHandling,
160+
DateParseHandling = settings.DateParseHandling,
161+
DateTimeZoneHandling = settings.DateTimeZoneHandling,
162+
DateFormatHandling = settings.DateFormatHandling,
163+
Formatting = settings.Formatting,
164+
MaxDepth = settings.MaxDepth,
165+
DateFormatString = settings.DateFormatString,
166+
Context = settings.Context,
167+
Error = settings.Error,
168+
SerializationBinder = settings.SerializationBinder,
169+
TraceWriter = settings.TraceWriter,
170+
Culture = settings.Culture,
171+
ReferenceResolverProvider = settings.ReferenceResolverProvider,
172+
EqualityComparer = settings.EqualityComparer,
173+
ContractResolver = settings.ContractResolver,
174+
ConstructorHandling = settings.ConstructorHandling,
175+
TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling,
176+
MetadataPropertyHandling = settings.MetadataPropertyHandling,
177+
TypeNameHandling = settings.TypeNameHandling,
178+
PreserveReferencesHandling = settings.PreserveReferencesHandling,
179+
Converters = settings.Converters,
180+
DefaultValueHandling = settings.DefaultValueHandling,
181+
NullValueHandling = settings.NullValueHandling,
182+
ObjectCreationHandling = settings.ObjectCreationHandling,
183+
MissingMemberHandling = settings.MissingMemberHandling,
184+
ReferenceLoopHandling = settings.ReferenceLoopHandling,
185+
CheckAdditionalContent = settings.CheckAdditionalContent,
186+
StringEscapeHandling = settings.StringEscapeHandling,
187+
};
188+
189+
return copiedSettings;
190+
}
156191
}
157192
}

src/Mvc/Mvc.Formatters.Json/test/JsonOutputFormatterTests.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ public void CanWriteResult_ReturnsExpectedValueForMediaType(
403403
{
404404
// Arrange
405405
var formatter = new JsonOutputFormatter(new JsonSerializerSettings(), ArrayPool<char>.Shared);
406-
406+
407407
var body = new MemoryStream();
408408
var actionContext = GetActionContext(MediaTypeHeaderValue.Parse(mediaType), body);
409409
var outputFormatterContext = new OutputFormatterWriteContext(
@@ -425,6 +425,40 @@ public void CanWriteResult_ReturnsExpectedValueForMediaType(
425425
Assert.Equal(new StringSegment(expectedContentType), outputFormatterContext.ContentType);
426426
}
427427

428+
[Fact]
429+
public async Task SerializingWithPreserveReferenceHandling()
430+
{
431+
// Arrange
432+
var expected = "{\"$id\":\"1\",\"fullName\":\"John\",\"age\":35}";
433+
var user = new User { FullName = "John", age = 35 };
434+
435+
var settings = new JsonSerializerSettings
436+
{
437+
ContractResolver = new DefaultContractResolver
438+
{
439+
NamingStrategy = new CamelCaseNamingStrategy(),
440+
},
441+
PreserveReferencesHandling = PreserveReferencesHandling.All,
442+
};
443+
var formatter = new TestableJsonOutputFormatter(settings);
444+
445+
for (var i = 0; i < 3; i++)
446+
{
447+
// Act
448+
var context = GetOutputFormatterContext(user, typeof(User));
449+
await formatter.WriteResponseBodyAsync(context, Encoding.UTF8);
450+
451+
// Assert
452+
var body = context.HttpContext.Response.Body;
453+
454+
Assert.NotNull(body);
455+
body.Position = 0;
456+
457+
var content = new StreamReader(body, Encoding.UTF8).ReadToEnd();
458+
Assert.Equal(expected, content);
459+
}
460+
}
461+
428462
private static Encoding CreateOrGetSupportedEncoding(
429463
JsonOutputFormatter formatter,
430464
string encodingAsString,

src/Mvc/Mvc.Formatters.Json/test/Microsoft.AspNetCore.Mvc.Formatters.Json.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8+
<Reference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" />
89
<Reference Include="Microsoft.AspNetCore.Mvc.TestCommon" />
910

1011
<Reference Include="Microsoft.AspNetCore.Http" />

0 commit comments

Comments
 (0)