Use System.Buffers for JSON.NET

This commit is contained in:
Ryan Nowak 2015-12-17 17:50:34 -08:00
parent 5ef839c855
commit 0a9804056e
8 changed files with 96 additions and 18 deletions

View File

@ -0,0 +1,39 @@
// 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;
using System.Buffers;
using Newtonsoft.Json;
namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal
{
public class JsonArrayPool<T> : IArrayPool<T>
{
private readonly ArrayPool<T> _inner;
public JsonArrayPool(ArrayPool<T> inner)
{
if (inner == null)
{
throw new ArgumentNullException(nameof(inner));
}
_inner = inner;
}
public T[] Rent(int minimumLength)
{
return _inner.Rent(minimumLength);
}
public void Return(T[] array)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
_inner.Return(array);
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Buffers;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -12,22 +13,26 @@ namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal
{ {
public class MvcJsonMvcOptionsSetup : ConfigureOptions<MvcOptions> public class MvcJsonMvcOptionsSetup : ConfigureOptions<MvcOptions>
{ {
public MvcJsonMvcOptionsSetup(ILoggerFactory loggerFactory, IOptions<MvcJsonOptions> jsonOptions) public MvcJsonMvcOptionsSetup(
: base((_) => ConfigureMvc(_, jsonOptions.Value.SerializerSettings, loggerFactory)) ILoggerFactory loggerFactory,
IOptions<MvcJsonOptions> jsonOptions,
ArrayPool<char> charPool)
: base((options) => ConfigureMvc(options, jsonOptions.Value.SerializerSettings, loggerFactory, charPool))
{ {
} }
public static void ConfigureMvc( public static void ConfigureMvc(
MvcOptions options, MvcOptions options,
JsonSerializerSettings serializerSettings, JsonSerializerSettings serializerSettings,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory,
ArrayPool<char> charPool)
{ {
var jsonInputLogger = loggerFactory.CreateLogger<JsonInputFormatter>(); var jsonInputLogger = loggerFactory.CreateLogger<JsonInputFormatter>();
var jsonInputPatchLogger = loggerFactory.CreateLogger<JsonPatchInputFormatter>(); var jsonInputPatchLogger = loggerFactory.CreateLogger<JsonPatchInputFormatter>();
options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings)); options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings, charPool));
options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings)); options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings, charPool));
options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings)); options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings, charPool));
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json")); options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));

View File

@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
using Microsoft.AspNet.Mvc.Formatters.Json.Logging; using Microsoft.AspNet.Mvc.Formatters.Json.Logging;
using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
@ -13,16 +15,22 @@ namespace Microsoft.AspNet.Mvc.Formatters
{ {
public class JsonInputFormatter : InputFormatter public class JsonInputFormatter : InputFormatter
{ {
private JsonSerializerSettings _serializerSettings; private readonly IArrayPool<char> _charPool;
private ILogger _logger; private readonly ILogger _logger;
private JsonSerializerSettings _serializerSettings;
public JsonInputFormatter(ILogger logger) public JsonInputFormatter(ILogger logger)
: this(logger, SerializerSettingsProvider.CreateSerializerSettings()) : this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{ {
} }
public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings) public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings)
: this(logger, serializerSettings, ArrayPool<char>.Shared)
{
}
public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
{ {
if (serializerSettings == null) if (serializerSettings == null)
{ {
@ -36,6 +44,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
_logger = logger; _logger = logger;
_serializerSettings = serializerSettings; _serializerSettings = serializerSettings;
_charPool = new JsonArrayPool<char>(charPool);
SupportedEncodings.Add(UTF8EncodingWithoutBOM); SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian); SupportedEncodings.Add(UTF16EncodingLittleEndian);
@ -84,6 +93,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{ {
using (var jsonReader = new JsonTextReader(streamReader)) using (var jsonReader = new JsonTextReader(streamReader))
{ {
jsonReader.ArrayPool = _charPool;
jsonReader.CloseInput = false; jsonReader.CloseInput = false;
var successful = true; var successful = true;

View File

@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.Internal;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -15,6 +17,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// </summary> /// </summary>
public class JsonOutputFormatter : OutputFormatter public class JsonOutputFormatter : OutputFormatter
{ {
private readonly IArrayPool<char> _charPool;
private JsonSerializerSettings _serializerSettings; private JsonSerializerSettings _serializerSettings;
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache // Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
@ -22,18 +26,29 @@ namespace Microsoft.AspNet.Mvc.Formatters
private JsonSerializer _serializer; private JsonSerializer _serializer;
public JsonOutputFormatter() public JsonOutputFormatter()
: this(SerializerSettingsProvider.CreateSerializerSettings()) : this(SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{ {
} }
public JsonOutputFormatter(JsonSerializerSettings serializerSettings) public JsonOutputFormatter(JsonSerializerSettings serializerSettings)
: this(serializerSettings, ArrayPool<char>.Shared)
{
}
public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
{ {
if (serializerSettings == null) if (serializerSettings == null)
{ {
throw new ArgumentNullException(nameof(serializerSettings)); throw new ArgumentNullException(nameof(serializerSettings));
} }
if (charPool == null)
{
throw new ArgumentNullException(nameof(charPool));
}
_serializerSettings = serializerSettings; _serializerSettings = serializerSettings;
_charPool = new JsonArrayPool<char>(charPool);
SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode); SupportedEncodings.Add(Encoding.Unicode);
@ -94,8 +109,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
throw new ArgumentNullException(nameof(writer)); throw new ArgumentNullException(nameof(writer));
} }
var jsonWriter = new JsonTextWriter(writer); var jsonWriter = new JsonTextWriter(writer)
jsonWriter.CloseOutput = false; {
ArrayPool = _charPool,
CloseOutput = false,
};
return jsonWriter; return jsonWriter;
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Buffers;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.JsonPatch; using Microsoft.AspNet.JsonPatch;
@ -14,12 +15,15 @@ namespace Microsoft.AspNet.Mvc.Formatters
public class JsonPatchInputFormatter : JsonInputFormatter public class JsonPatchInputFormatter : JsonInputFormatter
{ {
public JsonPatchInputFormatter(ILogger logger) public JsonPatchInputFormatter(ILogger logger)
: this(logger, SerializerSettingsProvider.CreateSerializerSettings()) : this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{ {
} }
public JsonPatchInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings) public JsonPatchInputFormatter(
: base(logger, serializerSettings) ILogger logger,
JsonSerializerSettings serializerSettings,
ArrayPool<char> charPool)
: base(logger, serializerSettings, charPool)
{ {
// Clear all values and only include json-patch+json value. // Clear all values and only include json-patch+json value.
SupportedMediaTypes.Clear(); SupportedMediaTypes.Clear();

View File

@ -16,7 +16,7 @@
"version": "1.0.0-*", "version": "1.0.0-*",
"type": "build" "type": "build"
}, },
"Newtonsoft.Json": "6.0.6" "Newtonsoft.Json": "8.0.1"
}, },
"frameworks": { "frameworks": {
"net451": {}, "net451": {},

View File

@ -112,7 +112,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider => services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider =>
{ {
var options = serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value; var options = serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
return new JsonOutputFormatter(options.SerializerSettings); var charPool = serviceProvider.GetRequiredService<ArrayPool<char>>();
return new JsonOutputFormatter(options.SerializerSettings, charPool);
})); }));
// //

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Buffers;
using Microsoft.AspNet.Mvc.DataAnnotations; using Microsoft.AspNet.Mvc.DataAnnotations;
using Microsoft.AspNet.Mvc.DataAnnotations.Internal; using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal; using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var loggerFactory = new LoggerFactory(); var loggerFactory = new LoggerFactory();
var serializerSettings = SerializerSettingsProvider.CreateSerializerSettings(); var serializerSettings = SerializerSettingsProvider.CreateSerializerSettings();
MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory); MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory, ArrayPool<char>.Shared);
} }
public MvcOptions Value { get; } public MvcOptions Value { get; }