Skip to content

Commit 23c02ef

Browse files
author
Pranav Krishnamoorthy
committed
Merged PR 7269: Avoid caching JsonSerializer
Avoid caching JsonSerializer
1 parent 45a57ea commit 23c02ef

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonOutputFormatter.cs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ public class NewtonsoftJsonOutputFormatter : TextOutputFormatter
2020
{
2121
private readonly IArrayPool<char> _charPool;
2222
private readonly MvcOptions _mvcOptions;
23-
24-
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
25-
// the serializer and invalidate it when the settings change.
26-
private JsonSerializer _serializer;
23+
private JsonSerializerSettings _serializerSettings;
2724

2825
/// <summary>
2926
/// Initializes a new <see cref="NewtonsoftJsonOutputFormatter"/> instance.
@@ -99,12 +96,13 @@ protected virtual JsonWriter CreateJsonWriter(TextWriter writer)
9996
/// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns>
10097
protected virtual JsonSerializer CreateJsonSerializer()
10198
{
102-
if (_serializer == null)
99+
if (_serializerSettings == null)
103100
{
104-
_serializer = JsonSerializer.Create(SerializerSettings);
101+
// Lock the serializer settings once the first serialization has been initiated.
102+
_serializerSettings = ShallowCopy(SerializerSettings);
105103
}
106104

107-
return _serializer;
105+
return JsonSerializer.Create(_serializerSettings);
108106
}
109107

110108
/// <summary>
@@ -166,5 +164,43 @@ public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext co
166164
}
167165
}
168166
}
167+
168+
private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
169+
{
170+
var copiedSettings = new JsonSerializerSettings
171+
{
172+
FloatParseHandling = settings.FloatParseHandling,
173+
FloatFormatHandling = settings.FloatFormatHandling,
174+
DateParseHandling = settings.DateParseHandling,
175+
DateTimeZoneHandling = settings.DateTimeZoneHandling,
176+
DateFormatHandling = settings.DateFormatHandling,
177+
Formatting = settings.Formatting,
178+
MaxDepth = settings.MaxDepth,
179+
DateFormatString = settings.DateFormatString,
180+
Context = settings.Context,
181+
Error = settings.Error,
182+
SerializationBinder = settings.SerializationBinder,
183+
TraceWriter = settings.TraceWriter,
184+
Culture = settings.Culture,
185+
ReferenceResolverProvider = settings.ReferenceResolverProvider,
186+
EqualityComparer = settings.EqualityComparer,
187+
ContractResolver = settings.ContractResolver,
188+
ConstructorHandling = settings.ConstructorHandling,
189+
TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling,
190+
MetadataPropertyHandling = settings.MetadataPropertyHandling,
191+
TypeNameHandling = settings.TypeNameHandling,
192+
PreserveReferencesHandling = settings.PreserveReferencesHandling,
193+
Converters = settings.Converters,
194+
DefaultValueHandling = settings.DefaultValueHandling,
195+
NullValueHandling = settings.NullValueHandling,
196+
ObjectCreationHandling = settings.ObjectCreationHandling,
197+
MissingMemberHandling = settings.MissingMemberHandling,
198+
ReferenceLoopHandling = settings.ReferenceLoopHandling,
199+
CheckAdditionalContent = settings.CheckAdditionalContent,
200+
StringEscapeHandling = settings.StringEscapeHandling,
201+
};
202+
203+
return copiedSettings;
204+
}
169205
}
170206
}

src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonOutputFormatterTest.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,40 @@ public async Task WriteToStreamAsync_LargePayload_DoesNotPerformSynchronousWrite
324324
stream.Verify(v => v.Flush(), Times.Never());
325325
}
326326

327+
[Fact]
328+
public async Task SerializingWithPreserveReferenceHandling()
329+
{
330+
// Arrange
331+
var expected = "{\"$id\":\"1\",\"fullName\":\"John\",\"age\":35}";
332+
var user = new User { FullName = "John", age = 35 };
333+
334+
var settings = new JsonSerializerSettings
335+
{
336+
ContractResolver = new DefaultContractResolver
337+
{
338+
NamingStrategy = new CamelCaseNamingStrategy(),
339+
},
340+
PreserveReferencesHandling = PreserveReferencesHandling.All,
341+
};
342+
var formatter = new TestableJsonOutputFormatter(settings);
343+
344+
for (var i = 0; i < 3; i++)
345+
{
346+
// Act
347+
var context = GetOutputFormatterContext(user, typeof(User));
348+
await formatter.WriteResponseBodyAsync(context, Encoding.UTF8);
349+
350+
// Assert
351+
var body = context.HttpContext.Response.Body;
352+
353+
Assert.NotNull(body);
354+
body.Position = 0;
355+
356+
var content = new StreamReader(body, Encoding.UTF8).ReadToEnd();
357+
Assert.Equal(expected, content);
358+
}
359+
}
360+
327361
private class TestableJsonOutputFormatter : NewtonsoftJsonOutputFormatter
328362
{
329363
public TestableJsonOutputFormatter(JsonSerializerSettings serializerSettings)

0 commit comments

Comments
 (0)