// 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 System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Mvc.Formatters
{
///
/// A for JSON content.
///
public class JsonOutputFormatter : TextOutputFormatter
{
private readonly IArrayPool _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;
///
/// Initializes a new instance.
///
///
/// The . Should be either the application-wide settings
/// () or an instance
/// initially returned.
///
/// The .
public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool charPool)
{
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
if (charPool == null)
{
throw new ArgumentNullException(nameof(charPool));
}
SerializerSettings = serializerSettings;
_charPool = new JsonArrayPool(charPool);
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson);
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson);
}
///
/// Gets the used to configure the .
///
///
/// Any modifications to the object after this
/// has been used will have no effect.
///
protected JsonSerializerSettings SerializerSettings { get; }
///
/// 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 .
///
/// The used to write.
/// The used during serialization.
protected virtual JsonWriter CreateJsonWriter(TextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
var jsonWriter = new JsonTextWriter(writer)
{
ArrayPool = _charPool,
CloseOutput = false,
};
return jsonWriter;
}
///
/// Called during serialization to create the .
///
/// The used during serialization and deserialization.
protected virtual JsonSerializer CreateJsonSerializer()
{
if (_serializer == null)
{
_serializer = JsonSerializer.Create(SerializerSettings);
}
return _serializer;
}
///
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (selectedEncoding == null)
{
throw new ArgumentNullException(nameof(selectedEncoding));
}
var response = context.HttpContext.Response;
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
{
WriteObject(writer, 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
// write).
await writer.FlushAsync();
}
}
}
}