Skip to content

Commit

Permalink
Merged PR 7268: Avoid caching JsonSerializer
Browse files Browse the repository at this point in the history
Avoid caching JsonSerializer
  • Loading branch information
Pranav Krishnamoorthy committed Apr 14, 2020
1 parent 1aa1069 commit 8d09403
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 8 deletions.
1 change: 1 addition & 0 deletions eng/PatchConfig.props
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Later on, this will be checked using this condition:
</PropertyGroup>
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.1.18' ">
<PackagesInPatch>
Microsoft.AspNetCore.Mvc.Formatters.Json;
</PackagesInPatch>
</PropertyGroup>
</Project>
49 changes: 42 additions & 7 deletions src/Mvc/Mvc.Formatters.Json/src/JsonOutputFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
public class JsonOutputFormatter : TextOutputFormatter
{
private readonly IArrayPool<char> _charPool;

// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
// the serializer and invalidate it when the settings change.
private JsonSerializer _serializer;
private JsonSerializerSettings _serializerSettings;

/// <summary>
/// Initializes a new <see cref="JsonOutputFormatter"/> instance.
Expand Down Expand Up @@ -121,12 +118,12 @@ protected virtual JsonWriter CreateJsonWriter(TextWriter writer)
/// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns>
protected virtual JsonSerializer CreateJsonSerializer()
{
if (_serializer == null)
if (_serializerSettings == null)
{
_serializer = JsonSerializer.Create(SerializerSettings);
_serializerSettings = ShallowCopy(SerializerSettings);
}

return _serializer;
return JsonSerializer.Create(_serializerSettings);
}

/// <inheritdoc />
Expand All @@ -153,5 +150,43 @@ public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext co
await writer.FlushAsync();
}
}

private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
{
var copiedSettings = new JsonSerializerSettings
{
FloatParseHandling = settings.FloatParseHandling,
FloatFormatHandling = settings.FloatFormatHandling,
DateParseHandling = settings.DateParseHandling,
DateTimeZoneHandling = settings.DateTimeZoneHandling,
DateFormatHandling = settings.DateFormatHandling,
Formatting = settings.Formatting,
MaxDepth = settings.MaxDepth,
DateFormatString = settings.DateFormatString,
Context = settings.Context,
Error = settings.Error,
SerializationBinder = settings.SerializationBinder,
TraceWriter = settings.TraceWriter,
Culture = settings.Culture,
ReferenceResolverProvider = settings.ReferenceResolverProvider,
EqualityComparer = settings.EqualityComparer,
ContractResolver = settings.ContractResolver,
ConstructorHandling = settings.ConstructorHandling,
TypeNameAssemblyFormatHandling = settings.TypeNameAssemblyFormatHandling,
MetadataPropertyHandling = settings.MetadataPropertyHandling,
TypeNameHandling = settings.TypeNameHandling,
PreserveReferencesHandling = settings.PreserveReferencesHandling,
Converters = settings.Converters,
DefaultValueHandling = settings.DefaultValueHandling,
NullValueHandling = settings.NullValueHandling,
ObjectCreationHandling = settings.ObjectCreationHandling,
MissingMemberHandling = settings.MissingMemberHandling,
ReferenceLoopHandling = settings.ReferenceLoopHandling,
CheckAdditionalContent = settings.CheckAdditionalContent,
StringEscapeHandling = settings.StringEscapeHandling,
};

return copiedSettings;
}
}
}
36 changes: 35 additions & 1 deletion src/Mvc/Mvc.Formatters.Json/test/JsonOutputFormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ public void CanWriteResult_ReturnsExpectedValueForMediaType(
{
// Arrange
var formatter = new JsonOutputFormatter(new JsonSerializerSettings(), ArrayPool<char>.Shared);

var body = new MemoryStream();
var actionContext = GetActionContext(MediaTypeHeaderValue.Parse(mediaType), body);
var outputFormatterContext = new OutputFormatterWriteContext(
Expand All @@ -425,6 +425,40 @@ public void CanWriteResult_ReturnsExpectedValueForMediaType(
Assert.Equal(new StringSegment(expectedContentType), outputFormatterContext.ContentType);
}

[Fact]
public async Task SerializingWithPreserveReferenceHandling()
{
// Arrange
var expected = "{\"$id\":\"1\",\"fullName\":\"John\",\"age\":35}";
var user = new User { FullName = "John", age = 35 };

var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(),
},
PreserveReferencesHandling = PreserveReferencesHandling.All,
};
var formatter = new TestableJsonOutputFormatter(settings);

for (var i = 0; i < 3; i++)
{
// Act
var context = GetOutputFormatterContext(user, typeof(User));
await formatter.WriteResponseBodyAsync(context, Encoding.UTF8);

// Assert
var body = context.HttpContext.Response.Body;

Assert.NotNull(body);
body.Position = 0;

var content = new StreamReader(body, Encoding.UTF8).ReadToEnd();
Assert.Equal(expected, content);
}
}

private static Encoding CreateOrGetSupportedEncoding(
JsonOutputFormatter formatter,
string encodingAsString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" />
<Reference Include="Microsoft.AspNetCore.Mvc.TestCommon" />

<Reference Include="Microsoft.AspNetCore.Http" />
Expand Down

0 comments on commit 8d09403

Please sign in to comment.