// 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.AspNet.Mvc.Formatters.Json.Internal;
using Microsoft.AspNet.Mvc.Internal;
using Newtonsoft.Json;
namespace Microsoft.AspNet.Mvc.Formatters
{
///
/// An output formatter that specializes in writing JSON content.
///
public class JsonOutputFormatter : OutputFormatter
{
private readonly IArrayPool _charPool;
private JsonSerializerSettings _serializerSettings;
// 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;
public JsonOutputFormatter()
: this(SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool.Shared)
{
}
public JsonOutputFormatter(JsonSerializerSettings serializerSettings)
: this(serializerSettings, ArrayPool.Shared)
{
}
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 or sets the used to configure the .
///
///
/// Any modifications to the object after this
/// has been used will have no effect.
///
public JsonSerializerSettings SerializerSettings
{
get
{
return _serializerSettings;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_serializerSettings = value;
// If the settings change, then invalidate the cached serializer.
_serializer = null;
}
}
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)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var response = context.HttpContext.Response;
var selectedEncoding = context.ContentType?.Encoding ?? Encoding.UTF8;
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();
}
}
}
}