diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/JsonHelperExtensions.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/JsonHelperExtensions.cs
index 4c83a279c7..0cab536616 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/JsonHelperExtensions.cs
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/JsonHelperExtensions.cs
@@ -23,6 +23,10 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
/// The to be used by the serializer.
///
/// A new containing the serialized JSON.
+ ///
+ /// The value for from
+ /// is ignored by this method and is always used.
+ ///
public static IHtmlContent Serialize(
this IJsonHelper jsonHelper,
object value,
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonHelper.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonHelper.cs
index 5179cdf7bc..d72aceee7d 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonHelper.cs
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonHelper.cs
@@ -6,8 +6,8 @@ using System.Buffers;
using System.Globalization;
using System.IO;
using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
@@ -17,42 +17,39 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
///
internal class NewtonsoftJsonHelper : IJsonHelper
{
- private readonly NewtonsoftJsonOutputFormatter _jsonOutputFormatter;
- private readonly ArrayPool _charPool;
+ // Perf: JsonSerializers are relatively expensive to create, and are thread safe. Cache the serializer
+ private readonly JsonSerializer _defaultSettingsJsonSerializer;
+ private readonly IArrayPool _charPool;
///
- /// Initializes a new instance of that is backed by .
+ /// Initializes a new instance of .
///
- /// The used to serialize JSON.
+ /// The .
///
/// The for use with custom (see
/// ).
///
- public NewtonsoftJsonHelper(NewtonsoftJsonOutputFormatter jsonOutputFormatter, ArrayPool charPool)
+ public NewtonsoftJsonHelper(IOptions options, ArrayPool charPool)
{
- if (jsonOutputFormatter == null)
+ if (options == null)
{
- throw new ArgumentNullException(nameof(jsonOutputFormatter));
+ throw new ArgumentNullException(nameof(options));
}
+
if (charPool == null)
{
throw new ArgumentNullException(nameof(charPool));
}
- _jsonOutputFormatter = jsonOutputFormatter;
- _charPool = charPool;
+ _defaultSettingsJsonSerializer = CreateHtmlSafeSerializer(options.Value.SerializerSettings);
+ _charPool = new JsonArrayPool(charPool);
}
- ///
public IHtmlContent Serialize(object value)
{
- var settings = ShallowCopy(_jsonOutputFormatter.PublicSerializerSettings);
- settings.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
-
- return Serialize(value, settings);
+ return Serialize(value, _defaultSettingsJsonSerializer);
}
- ///
public IHtmlContent Serialize(object value, JsonSerializerSettings serializerSettings)
{
if (serializerSettings == null)
@@ -60,54 +57,34 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
throw new ArgumentNullException(nameof(serializerSettings));
}
- var jsonOutputFormatter = new NewtonsoftJsonOutputFormatter(serializerSettings, _charPool);
-
- return SerializeInternal(jsonOutputFormatter, value);
+ var jsonSerializer = CreateHtmlSafeSerializer(serializerSettings);
+ return Serialize(value, jsonSerializer);
}
- private IHtmlContent SerializeInternal(NewtonsoftJsonOutputFormatter jsonOutputFormatter, object value)
+ private IHtmlContent Serialize(object value, JsonSerializer jsonSerializer)
{
- var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
- jsonOutputFormatter.WriteObject(stringWriter, value);
-
- return new HtmlString(stringWriter.ToString());
- }
-
- private static JsonSerializerSettings ShallowCopy(JsonSerializerSettings settings)
- {
- var copiedSettings = new JsonSerializerSettings
+ using (var stringWriter = new StringWriter(CultureInfo.InvariantCulture))
{
- 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,
- };
+ var jsonWriter = new JsonTextWriter(stringWriter)
+ {
+ ArrayPool = _charPool,
+ };
- return copiedSettings;
+ using (jsonWriter)
+ {
+ jsonSerializer.Serialize(jsonWriter, value);
+ }
+
+ return new HtmlString(stringWriter.ToString());
+ }
+ }
+
+ private static JsonSerializer CreateHtmlSafeSerializer(JsonSerializerSettings serializerSettings)
+ {
+ var jsonSerializer = JsonSerializer.Create(serializerSettings);
+ // Ignore the user configured StringEscapeHandling and always escape it.
+ jsonSerializer.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
+ return jsonSerializer;
}
}
}
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonOutputFormatter.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonOutputFormatter.cs
index 57825099ad..382ca577ff 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonOutputFormatter.cs
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonOutputFormatter.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@@ -63,36 +62,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
///
protected JsonSerializerSettings SerializerSettings { get; }
- ///
- /// Gets the used to configure the .
- ///
- ///
- /// Any modifications to the object after this
- /// has been used will have no effect.
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public JsonSerializerSettings PublicSerializerSettings => SerializerSettings;
-
- ///
- /// Writes the given as JSON using the given
- /// .
- ///
- /// The used to write the
- /// The value to write as JSON.
- public void WriteObject(TextWriter writer, object value)
- {
- if (writer == null)
- {
- throw new ArgumentNullException(nameof(writer));
- }
-
- using (var jsonWriter = CreateJsonWriter(writer))
- {
- var jsonSerializer = CreateJsonSerializer();
- jsonSerializer.Serialize(jsonWriter, value);
- }
- }
-
///
/// Called during serialization to create the .
///
@@ -145,7 +114,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
var response = context.HttpContext.Response;
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
{
- WriteObject(writer, context.Object);
+ using (var jsonWriter = CreateJsonWriter(writer))
+ {
+ var jsonSerializer = CreateJsonSerializer();
+ jsonSerializer.Serialize(jsonWriter, context.Object);
+ }
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
// buffers. This is better than just letting dispose handle it (which would result in a synchronous
diff --git a/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs b/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs
index d17c06b9d9..102dbcd811 100644
--- a/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs
+++ b/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/BasicTests.cs
@@ -265,7 +265,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public async Task JsonHelperWithSettings_RendersJson_WithNamesUnchanged()
{
// Arrange
- var json = "{\"id\":9000,\"FullName\":\"John Smith\"}";
+ var json = "{\"id\":9000,\"FullName\":\"John \\u003cb\\u003eSmith\\u003c/b\\u003e\"}";
var expectedBody = string.Format(
@"