// 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.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.AspNet.Mvc.Formatters.Xml;
using Microsoft.AspNet.Mvc.Formatters.Xml.Internal;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Formatters
{
///
/// This class handles serialization of objects
/// to XML using
///
public class XmlSerializerOutputFormatter : OutputFormatter
{
private ConcurrentDictionary _serializerCache = new ConcurrentDictionary();
///
/// Initializes a new instance of
/// with default XmlWriterSettings.
///
public XmlSerializerOutputFormatter() :
this(FormattingUtilities.GetDefaultXmlWriterSettings())
{
}
///
/// Initializes a new instance of
///
/// The settings to be used by the .
public XmlSerializerOutputFormatter([NotNull] XmlWriterSettings writerSettings)
{
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationXml);
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextXml);
WriterSettings = writerSettings;
WrapperProviderFactories = new List();
WrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(WrapperProviderFactories));
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
///
/// Gets the list of to
/// provide the wrapping type for serialization.
///
public IList WrapperProviderFactories { get; }
///
/// Gets the settings to be used by the XmlWriter.
///
public XmlWriterSettings WriterSettings { get; }
///
/// Gets the type of the object to be serialized.
///
/// The declared type of the object.
/// The runtime type of the object
/// A type that needs to be serialized.
protected virtual Type ResolveType(Type declaredType, Type runtimeType)
{
if (declaredType == null || declaredType == typeof(object))
{
if (runtimeType != null)
{
return runtimeType;
}
}
return declaredType;
}
///
/// Gets the type to be serialized.
///
/// The original type to be serialized
/// The original or wrapped type provided by any .
protected virtual Type GetSerializableType(Type type)
{
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(type, isSerialization: true));
return wrapperProvider?.WrappingType ?? type;
}
///
protected override bool CanWriteType(Type declaredType, Type runtimeType)
{
var type = ResolveType(declaredType, runtimeType);
if (type == null)
{
return false;
}
return GetCachedSerializer(GetSerializableType(type)) != null;
}
///
/// Create a new instance of for the given object type.
///
/// The type of object for which the serializer should be created.
/// A new instance of
protected virtual XmlSerializer CreateSerializer([NotNull] Type type)
{
try
{
// If the serializer does not support this type it will throw an exception.
return new XmlSerializer(type);
}
catch (Exception)
{
// We do not surface the caught exception because if CanWriteResult returns
// false, then this Formatter is not picked up at all.
return null;
}
}
///
/// Creates a new instance of using the given stream and the .
///
/// The stream on which the XmlWriter should operate on.
/// A new instance of
public virtual XmlWriter CreateXmlWriter(
[NotNull] Stream writeStream,
[NotNull] XmlWriterSettings xmlWriterSettings)
{
return XmlWriter.Create(
new HttpResponseStreamWriter(writeStream, xmlWriterSettings.Encoding),
xmlWriterSettings);
}
///
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
{
var response = context.HttpContext.Response;
var tempWriterSettings = WriterSettings.Clone();
tempWriterSettings.Encoding = context.SelectedEncoding;
using (var xmlWriter = CreateXmlWriter(context.HttpContext.Response.Body, tempWriterSettings))
{
var obj = context.Object;
var runtimeType = obj?.GetType();
var resolvedType = ResolveType(context.DeclaredType, runtimeType);
var wrappingType = GetSerializableType(resolvedType);
// Wrap the object only if there is a wrapping type.
if (wrappingType != null && wrappingType != resolvedType)
{
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(
declaredType: resolvedType,
isSerialization: true));
obj = wrapperProvider.Wrap(obj);
}
var xmlSerializer = GetCachedSerializer(wrappingType);
xmlSerializer.Serialize(xmlWriter, obj);
}
return Task.FromResult(true);
}
///
/// Gets the cached serializer or creates and caches the serializer for the given type.
///
/// The instance.
protected virtual XmlSerializer GetCachedSerializer(Type type)
{
object serializer;
if (!_serializerCache.TryGetValue(type, out serializer))
{
serializer = CreateSerializer(type);
if (serializer != null)
{
_serializerCache.TryAdd(type, serializer);
}
}
return (XmlSerializer)serializer;
}
}
}