// 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; } } }