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.
// 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.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -12,22 +13,26 @@ namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal
{
public class MvcJsonMvcOptionsSetup : ConfigureOptions<MvcOptions>
{
public MvcJsonMvcOptionsSetup(ILoggerFactory loggerFactory, IOptions<MvcJsonOptions> jsonOptions)
: base((_) => ConfigureMvc(_, jsonOptions.Value.SerializerSettings, loggerFactory))
public MvcJsonMvcOptionsSetup(
ILoggerFactory loggerFactory,
IOptions<MvcJsonOptions> jsonOptions,
ArrayPool<char> charPool)
: base((options) => ConfigureMvc(options, jsonOptions.Value.SerializerSettings, loggerFactory, charPool))
{
}
public static void ConfigureMvc(
MvcOptions options,
JsonSerializerSettings serializerSettings,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
ArrayPool<char> charPool)
{
var jsonInputLogger = loggerFactory.CreateLogger<JsonInputFormatter>();
var jsonInputPatchLogger = loggerFactory.CreateLogger<JsonPatchInputFormatter>();
options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings));
options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings));
options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings));
options.OutputFormatters.Add(new JsonOutputFormatter(serializerSettings, charPool));
options.InputFormatters.Add(new JsonInputFormatter(jsonInputLogger, serializerSettings, charPool));
options.InputFormatters.Add(new JsonPatchInputFormatter(jsonInputPatchLogger, serializerSettings, charPool));
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.
using System;
using System.Buffers;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
using Microsoft.AspNet.Mvc.Formatters.Json.Logging;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -13,16 +15,22 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
public class JsonInputFormatter : InputFormatter
{
private JsonSerializerSettings _serializerSettings;
private ILogger _logger;
private readonly IArrayPool<char> _charPool;
private readonly ILogger _logger;
private JsonSerializerSettings _serializerSettings;
public JsonInputFormatter(ILogger logger)
: this(logger, SerializerSettingsProvider.CreateSerializerSettings())
: this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{
}
public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings)
: this(logger, serializerSettings, ArrayPool<char>.Shared)
{
}
public JsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
{
if (serializerSettings == null)
{
@ -36,6 +44,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
_logger = logger;
_serializerSettings = serializerSettings;
_charPool = new JsonArrayPool<char>(charPool);
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian);
@ -84,6 +93,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
using (var jsonReader = new JsonTextReader(streamReader))
{
jsonReader.ArrayPool = _charPool;
jsonReader.CloseInput = false;
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.
using System;
using System.Buffers;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
using Microsoft.AspNet.Mvc.Internal;
using Newtonsoft.Json;
@ -15,6 +17,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// </summary>
public class JsonOutputFormatter : OutputFormatter
{
private readonly IArrayPool<char> _charPool;
private JsonSerializerSettings _serializerSettings;
// 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;
public JsonOutputFormatter()
: this(SerializerSettingsProvider.CreateSerializerSettings())
: this(SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{
}
public JsonOutputFormatter(JsonSerializerSettings serializerSettings)
: this(serializerSettings, ArrayPool<char>.Shared)
{
}
public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
{
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
if (charPool == null)
{
throw new ArgumentNullException(nameof(charPool));
}
_serializerSettings = serializerSettings;
_charPool = new JsonArrayPool<char>(charPool);
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
@ -94,8 +109,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
throw new ArgumentNullException(nameof(writer));
}
var jsonWriter = new JsonTextWriter(writer);
jsonWriter.CloseOutput = false;
var jsonWriter = new JsonTextWriter(writer)
{
ArrayPool = _charPool,
CloseOutput = false,
};
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.
using System;
using System.Buffers;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.JsonPatch;
@ -14,12 +15,15 @@ namespace Microsoft.AspNet.Mvc.Formatters
public class JsonPatchInputFormatter : JsonInputFormatter
{
public JsonPatchInputFormatter(ILogger logger)
: this(logger, SerializerSettingsProvider.CreateSerializerSettings())
: this(logger, SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
{
}
public JsonPatchInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings)
: base(logger, serializerSettings)
public JsonPatchInputFormatter(
ILogger logger,
JsonSerializerSettings serializerSettings,
ArrayPool<char> charPool)
: base(logger, serializerSettings, charPool)
{
// Clear all values and only include json-patch+json value.
SupportedMediaTypes.Clear();

View File

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

View File

@ -112,7 +112,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider =>
{
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.
// 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.Internal;
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var loggerFactory = new LoggerFactory();
var serializerSettings = SerializerSettingsProvider.CreateSerializerSettings();
MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory);
MvcJsonMvcOptionsSetup.ConfigureMvc(Value, serializerSettings, loggerFactory, ArrayPool<char>.Shared);
}
public MvcOptions Value { get; }