[Fixes #1121,#1508] Wrap objects for serialization/deserialization and DelegatingEnumerable<> support for IEnumerable<T> and IQueryable<T>

This commit is contained in:
Kiran Challa 2015-01-20 15:22:54 -08:00
parent 7c7eaa264d
commit e5176d22f6
58 changed files with 2316 additions and 189 deletions

View File

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Serializes <see cref="IEnumerable{T}"/> types by delegating them through a concrete implementation.
/// </summary>
/// <typeparam name="TWrapped">The wrapping or original type of the <see cref="IEnumerable{T}"/>
/// to proxy.</typeparam>
/// <typeparam name="TDeclared">The type parameter of the original <see cref="IEnumerable{T}"/>
/// to proxy.</typeparam>
public class DelegatingEnumerable<TWrapped, TDeclared> : IEnumerable<TWrapped>
{
private readonly IEnumerable<TDeclared> _source;
private readonly IWrapperProvider _wrapperProvider;
/// <summary>
/// Initializes a <see cref="DelegatingEnumerable{TWrapped, TDeclared}"/>.
/// </summary>
/// <remarks>
/// This constructor is necessary for <see cref="System.Runtime.Serialization.DataContractSerializer"/>
/// to serialize.
/// </remarks>
public DelegatingEnumerable()
{
_source = Enumerable.Empty<TDeclared>();
}
/// <summary>
/// Initializes a <see cref="DelegatingEnumerable{TWrapped, TDeclared}"/> with the original
/// <see cref="IEnumerable{T}"/> and the wrapper provider for wrapping individual elements.
/// </summary>
/// <param name="source">The <see cref="IEnumerable{T}"/> instance to get the enumerator from.</param>
/// <param name="elementWrapperProvider">The wrapper provider for wrapping individual elements.</param>
public DelegatingEnumerable([NotNull] IEnumerable<TDeclared> source, IWrapperProvider elementWrapperProvider)
{
_source = source;
_wrapperProvider = elementWrapperProvider;
}
/// <summary>
/// Gets a delegating enumerator of the original <see cref="IEnumerable{T}"/> source which is being
/// wrapped.
/// </summary>
/// <returns>The delegating enumerator of the original <see cref="IEnumerable{T}"/> source.</returns>
public IEnumerator<TWrapped> GetEnumerator()
{
return new DelegatingEnumerator<TWrapped, TDeclared>(_source.GetEnumerator(), _wrapperProvider);
}
/// <summary>
/// The serializer requires every type it encounters can be serialized and deserialized.
/// This type will never be used for deserialization, but we are required to implement the add
/// method so that the type can be serialized. This will never be called.
/// </summary>
/// <param name="item">The item to add. Unused.</param>
public void Add(object item)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets a delegating enumerator of the original <see cref="IEnumerable{T}"/> source which is being
/// wrapped.
/// </summary>
/// <returns>The delegating enumerator of the original <see cref="IEnumerable{T}"/> source.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,77 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Delegates enumeration of elements to the original enumerator and wraps the items
/// with the supplied <see cref="IWrapperProvider"/>.
/// </summary>
/// <typeparam name="TWrapped">The type to which the individual elements need to be wrapped to.</typeparam>
/// <typeparam name="TDeclared">The original type of the element being wrapped.</typeparam>
public class DelegatingEnumerator<TWrapped, TDeclared> : IEnumerator<TWrapped>
{
private readonly IEnumerator<TDeclared> _inner;
private readonly IWrapperProvider _wrapperProvider;
/// <summary>
/// Initializes a <see cref="DelegatingEnumerable{TWrapped, TDeclared}"/> which enumerates
/// over the elements of the original enumerator and wraps them using the supplied
/// <see cref="IWrapperProvider"/>.
/// </summary>
/// <param name="inner">The original enumerator.</param>
/// <param name="wrapperProvider">The wrapper provider to wrap individual elements.</param>
public DelegatingEnumerator([NotNull] IEnumerator<TDeclared> inner, IWrapperProvider wrapperProvider)
{
_inner = inner;
_wrapperProvider = wrapperProvider;
}
/// <inheritdoc />
public TWrapped Current
{
get
{
object obj = _inner.Current;
if (_wrapperProvider == null)
{
// if there is no wrapper, then this cast should not fail
return (TWrapped)obj;
}
return (TWrapped)_wrapperProvider.Wrap(obj);
}
}
/// <inheritdoc />
object IEnumerator.Current
{
get
{
return Current;
}
}
/// <inheritdoc />
public void Dispose()
{
_inner.Dispose();
}
/// <inheritdoc />
public bool MoveNext()
{
return _inner.MoveNext();
}
/// <inheritdoc />
public void Reset()
{
_inner.Reset();
}
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Provides a <see cref="IWrapperProvider"/> for interface types which implement
/// <see cref="IEnumerable{T}"/>.
/// </summary>
public class EnumerableWrapperProvider : IWrapperProvider
{
private readonly IWrapperProvider _wrapperProvider;
private readonly ConstructorInfo _wrappingTypeConstructor;
/// <summary>
/// Initializes an instance of <see cref="EnumerableWrapperProvider"/>.
/// </summary>
/// <param name="sourceEnumerableOfT">Type of the original <see cref="IEnumerable{T}" />
/// that is being wrapped.</param>
/// <param name="elementWrapperProvider">The <see cref="IWrapperProvider"/> for the element type.
/// Can be null.</param>
public EnumerableWrapperProvider(
[NotNull] Type sourceEnumerableOfT,
IWrapperProvider elementWrapperProvider)
{
var enumerableOfT = sourceEnumerableOfT.ExtractGenericInterface(typeof(IEnumerable<>));
if (!sourceEnumerableOfT.IsInterface() || enumerableOfT == null)
{
throw new ArgumentException(
Resources.FormatEnumerableWrapperProvider_InvalidSourceEnumerableOfT(typeof(IEnumerable<>).Name),
nameof(sourceEnumerableOfT));
}
_wrapperProvider = elementWrapperProvider;
var declaredElementType = enumerableOfT.GetGenericArguments()[0];
var wrappedElementType = elementWrapperProvider?.WrappingType ?? declaredElementType;
WrappingType = typeof(DelegatingEnumerable<,>).MakeGenericType(wrappedElementType, declaredElementType);
_wrappingTypeConstructor = WrappingType.GetConstructor(new[]
{
sourceEnumerableOfT,
typeof(IWrapperProvider)
});
}
/// <inheritdoc />
public Type WrappingType
{
get;
}
/// <inheritdoc />
public object Wrap(object original)
{
if (original == null)
{
return null;
}
return _wrappingTypeConstructor.Invoke(new object[] { original, _wrapperProvider });
}
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Creates an <see cref="EnumerableWrapperProvider"/> for interface types implementing the
/// <see cref="IEnumerable{T}"/> type.
/// </summary>
public class EnumerableWrapperProviderFactory : IWrapperProviderFactory
{
private readonly IEnumerable<IWrapperProviderFactory> _wrapperProviderFactories;
/// <summary>
/// Initializes an <see cref="EnumerableWrapperProviderFactory"/> with a list
/// <see cref="IWrapperProviderFactory"/>.
/// </summary>
/// <param name="wrapperProviderFactories">List of <see cref="IWrapperProviderFactory"/>.</param>
public EnumerableWrapperProviderFactory([NotNull] IEnumerable<IWrapperProviderFactory> wrapperProviderFactories)
{
_wrapperProviderFactories = wrapperProviderFactories;
}
/// <summary>
/// Gets an <see cref="EnumerableWrapperProvider"/> for the provided context.
/// </summary>
/// <param name="context">The <see cref="WrapperProviderContext"/>.</param>
/// <returns>An instance of <see cref="EnumerableWrapperProvider"/> if the declared type is
/// an interface and implements <see cref="IEnumerable{T}"/>.</returns>
public IWrapperProvider GetProvider([NotNull] WrapperProviderContext context)
{
if (context.IsSerialization)
{
// Example: IEnumerable<SerializableError>
var declaredType = context.DeclaredType;
// We only wrap interfaces types(ex: IEnumerable<T>, IQueryable<T>, IList<T> etc.) and not
// concrete types like List<T>, Collection<T> which implement IEnumerable<T>.
if (declaredType != null && declaredType.IsInterface() && declaredType.IsGenericType())
{
var enumerableOfT = declaredType.ExtractGenericInterface(typeof(IEnumerable<>));
if (enumerableOfT != null)
{
var elementType = enumerableOfT.GetGenericArguments()[0];
var wrapperProviderContext = new WrapperProviderContext(
elementType,
context.IsSerialization);
var elementWrapperProvider = _wrapperProviderFactories.GetWrapperProvider(wrapperProviderContext);
return new EnumerableWrapperProvider(enumerableOfT, elementWrapperProvider);
}
}
}
return null;
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Defines an interface for objects to be un-wrappable after deserialization.
/// </summary>
public interface IUnwrappable
{
/// <summary>
/// Unwraps an object.
/// </summary>
/// <param name="declaredType">The type to which the object should be un-wrapped to.</param>
/// <returns>The un-wrapped object.</returns>
object Unwrap(Type declaredType);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Defines an interface for wrapping objects for serialization or deserialization into xml.
/// </summary>
public interface IWrapperProvider
{
/// <summary>
/// Gets the wrapping type.
/// </summary>
Type WrappingType { get; }
/// <summary>
/// Wraps the given object to the wrapping type provided by <see cref="WrappingType"/>.
/// </summary>
/// <param name="original">The original non-wrapped object.</param>
/// <returns>Returns a wrapped object.</returns>
object Wrap(object original);
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Create a <see cref="IWrapperProvider"/> given a <see cref="WrapperProviderContext"/>.
/// </summary>
public interface IWrapperProviderFactory
{
/// <summary>
/// Gets the <see cref="IWrapperProvider"/> for the provided context.
/// </summary>
/// <param name="context">The <see cref="WrapperProviderContext"/>.</param>
/// <returns>A wrapping provider if the factory decides to wrap the type, else <c>null</c>.</returns>
IWrapperProvider GetProvider(WrapperProviderContext context);
}
}

View File

@ -0,0 +1,62 @@
// <auto-generated />
namespace Microsoft.AspNet.Mvc.Xml
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Mvc.Xml.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The type must be an interface and must be or derive from '{0}'.
/// </summary>
internal static string EnumerableWrapperProvider_InvalidSourceEnumerableOfT
{
get { return GetString("EnumerableWrapperProvider_InvalidSourceEnumerableOfT"); }
}
/// <summary>
/// The type must be an interface and must be or derive from '{0}'.
/// </summary>
internal static string FormatEnumerableWrapperProvider_InvalidSourceEnumerableOfT(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("EnumerableWrapperProvider_InvalidSourceEnumerableOfT"), p0);
}
/// <summary>
/// The object to be wrapped must be of type '{0}' but was of type '{1}'.
/// </summary>
internal static string WrapperProvider_MismatchType
{
get { return GetString("WrapperProvider_MismatchType"); }
}
/// <summary>
/// The object to be wrapped must be of type '{0}' but was of type '{1}'.
/// </summary>
internal static string FormatWrapperProvider_MismatchType(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("WrapperProvider_MismatchType"), p0, p1);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="EnumerableWrapperProvider_InvalidSourceEnumerableOfT" xml:space="preserve">
<value>The type must be an interface and must be or derive from '{0}'.</value>
</data>
<data name="WrapperProvider_MismatchType" xml:space="preserve">
<value>The object to be wrapped must be of type '{0}' but was of type '{1}'.</value>
</data>
</root>

View File

@ -1,4 +1,7 @@
using System;
// Copyright (c) Microsoft Open Technologies, Inc. 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.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
@ -9,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Wrapper class for <see cref="SerializableError"/> to enable it to be serialized by the xml formatters.
/// </summary>
[XmlRoot("Error")]
public sealed class SerializableErrorWrapper : IXmlSerializable
public sealed class SerializableErrorWrapper : IXmlSerializable, IUnwrappable
{
// Note: XmlSerializer requires to have default constructor
public SerializableErrorWrapper()
@ -83,61 +86,10 @@ namespace Microsoft.AspNet.Mvc.Xml
}
}
/// <summary>
/// Gets the
/// </summary>
/// <param name="modelType"></param>
/// <param name="deserializedObject"></param>
/// <returns></returns>
public static object UnwrapSerializableErrorObject([NotNull] Type modelType, object deserializedObject)
/// <inheritdoc />
public object Unwrap([NotNull] Type declaredType)
{
// Since we expect users to typically bind with SerializableError type,
// we should try to unwrap and get the actual SerializableError.
if (modelType == typeof(SerializableError))
{
var serializableErrorWrapper = deserializedObject as SerializableErrorWrapper;
if (serializableErrorWrapper != null)
{
deserializedObject = serializableErrorWrapper.SerializableError;
}
}
return deserializedObject;
}
/// <summary>
/// Checks if an object is an instance of type <see cref="SerializableError"/> and if yes,
/// gets and returns the wrapped <see cref="SerializableErrorWrapper"/> object in it.
/// </summary>
/// <param name="obj">An </param>
/// <returns></returns>
public static object WrapSerializableErrorObject(object obj)
{
var serializableError = obj as SerializableError;
if (serializableError == null)
{
return obj;
}
return new SerializableErrorWrapper(serializableError);
}
/// <summary>
/// Checks if the given type is of type <see cref="SerializableError"/> and if yes, returns
/// the wrapper type <see cref="SerializableErrorWrapper"/>.
/// </summary>
/// <param name="type">The type to be checked</param>
/// <returns><see cref="SerializableErrorWrapper"/> type, else the original type.</returns>
public static Type CreateSerializableType([NotNull] Type type)
{
// Since the type "SerializableError" is not compatible
// with the xml serializers, we create a compatible wrapper type for serialization.
if (type == typeof(SerializableError))
{
type = typeof(SerializableErrorWrapper);
}
return type;
return SerializableError;
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Wraps the object of type <see cref="Microsoft.AspNet.Mvc.SerializableError"/>.
/// </summary>
public class SerializableErrorWrapperProvider : IWrapperProvider
{
/// <inheritdoc />
public Type WrappingType
{
get
{
return typeof(SerializableErrorWrapper);
}
}
/// <inheritdoc />
public object Wrap([NotNull] object original)
{
var error = original as SerializableError;
if (error == null)
{
throw new ArgumentException(
Resources.FormatWrapperProvider_MismatchType(
typeof(SerializableErrorWrapper).Name,
original.GetType().Name),
nameof(original));
}
return new SerializableErrorWrapper(error);
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Creates an <see cref="IWrapperProvider"/> for the type <see cref="Microsoft.AspNet.Mvc.SerializableError"/>.
/// </summary>
public class SerializableErrorWrapperProviderFactory : IWrapperProviderFactory
{
/// <summary>
/// Creates an instance of <see cref="SerializableErrorWrapperProvider"/> if the provided
/// declared type is <see cref="Microsoft.AspNet.Mvc.SerializableError"/>.
/// </summary>
/// <param name="context"></param>
/// <returns>An instance of <see cref="SerializableErrorWrapperProvider"/> if the provided
/// declared type is <see cref="Microsoft.AspNet.Mvc.SerializableError"/>, else <c>null</c>.</returns>
public IWrapperProvider GetProvider([NotNull] WrapperProviderContext context)
{
if (context.DeclaredType == typeof(SerializableError))
{
return new SerializableErrorWrapperProvider();
}
return null;
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// The context used by an <see cref="IWrapperProvider"/> to wrap or un-wrap types.
/// </summary>
public class WrapperProviderContext
{
/// <summary>
/// Initializes a <see cref="WrapperProviderContext"/>.
/// </summary>
/// <param name="declaredType">The declared type of the object that needs to be wrapped.</param>
/// <param name="isSerialization"><see langword="true"/> if the wrapper provider is invoked during
/// serialization, otherwise <see langword="false"/>.</param>
public WrapperProviderContext([NotNull] Type declaredType, bool isSerialization)
{
DeclaredType = declaredType;
IsSerialization = isSerialization;
}
/// <summary>
/// The declared type which could be wrapped/un-wrapped by a different type
/// during serialization or de-serializatoin.
/// </summary>
public Type DeclaredType { get; }
/// <summary>
/// <see langword="true"/> if a wrapper provider is invoked during serialization,
/// <see langword="false"/> otherwise.
/// </summary>
public bool IsSerialization { get; }
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.Xml
{
/// <summary>
/// Extension methods for <see cref="IWrapperProviderFactory"/>.
/// </summary>
public static class WrapperProviderFactoriesExtensions
{
/// <summary>
/// Gets an instance of <see cref="IWrapperProvider"/> for the supplied
/// type.
/// </summary>
/// <param name="wrapperProviderFactories">A list of <see cref="IWrapperProviderFactory"/>.</param>
/// <param name="wrapperProviderContext">The <see cref="WrapperProviderContext"/>.</param>
/// <returns>An instance of <see cref="IWrapperProvider"/> if there is a wrapping provider for the
/// supplied type, else null.</returns>
public static IWrapperProvider GetWrapperProvider(
[NotNull] this IEnumerable<IWrapperProviderFactory> wrapperProviderFactories,
[NotNull] WrapperProviderContext wrapperProviderContext)
{
foreach (var wrapperProviderFactory in wrapperProviderFactories)
{
var wrapperProvider = wrapperProviderFactory.GetProvider(wrapperProviderContext);
if (wrapperProvider != null)
{
return wrapperProvider;
}
}
return null;
}
}
}

View File

@ -31,17 +31,28 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedEncodings = new List<Encoding>();
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
_serializerSettings = new DataContractSerializerSettings();
WrapperProviderFactories = new List<IWrapperProviderFactory>();
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <inheritdoc />
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for de-serialization.
/// </summary>
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
/// <inheritdoc />
public IList<Encoding> SupportedEncodings { get; private set; }
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
/// <inheritdoc />
public IList<Encoding> SupportedEncodings { get; }
/// <summary>
/// Indicates the acceptable input XML depth.
@ -127,7 +138,10 @@ namespace Microsoft.AspNet.Mvc.Xml
/// <returns>The type to which the XML will be deserialized.</returns>
protected virtual Type GetSerializableType([NotNull] Type declaredType)
{
return SerializableErrorWrapper.CreateSerializableType(declaredType);
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(declaredType, isSerialization: false));
return wrapperProvider?.WrappingType ?? declaredType;
}
/// <summary>
@ -157,9 +171,21 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{
var type = GetSerializableType(context.ModelType);
var dataContractSerializer = CreateSerializer(type);
var deserializedObject = dataContractSerializer.ReadObject(xmlReader);
deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
var serializer = CreateSerializer(type);
var deserializedObject = serializer.ReadObject(xmlReader);
// Unwrap only if the original type was wrapped.
if (type != context.ModelType)
{
var unwrappable = deserializedObject as IUnwrappable;
if (unwrappable != null)
{
deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType);
}
}
return Task.FromResult(deserializedObject);
}
}

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.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
@ -40,9 +41,20 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
_serializerSettings = new DataContractSerializerSettings();
WrapperProviderFactories = new List<IWrapperProviderFactory>();
WrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(WrapperProviderFactories));
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for serialization.
/// </summary>
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
/// <summary>
/// Gets the settings to be used by the XmlWriter.
/// </summary>
@ -72,24 +84,38 @@ namespace Microsoft.AspNet.Mvc.Xml
/// <param name="declaredType">The declared type.</param>
/// <param name="runtimeType">The runtime type.</param>
/// <returns>The type of the object to be serialized.</returns>
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
protected virtual Type ResolveType(Type declaredType, Type runtimeType)
{
Type type = declaredType;
if (declaredType == null || declaredType == typeof(object))
{
if (runtimeType != null)
{
type = runtimeType;
return runtimeType;
}
}
return SerializableErrorWrapper.CreateSerializableType(type);
return declaredType;
}
/// <summary>
/// Gets the type to be serialized.
/// </summary>
/// <param name="type">The original type to be serialized</param>
/// <returns>The original or wrapped type provided by any <see cref="IWrapperProvider"/>s.</returns>
protected virtual Type GetSerializableType(Type type)
{
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(type, isSerialization: true));
return wrapperProvider?.WrappingType ?? type;
}
/// <inheritdoc />
protected override bool CanWriteType(Type declaredType, Type runtimeType)
{
return CreateSerializer(GetSerializableType(declaredType, runtimeType)) != null;
var type = ResolveType(declaredType, runtimeType);
return CreateSerializer(GetSerializableType(type)) != null;
}
/// <summary>
@ -138,12 +164,26 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var outputStream = new DelegatingStream(innerStream))
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
{
var runtimeType = context.Object == null ? null : context.Object.GetType();
var obj = context.Object;
var runtimeType = obj?.GetType();
var type = GetSerializableType(context.DeclaredType, runtimeType);
var dataContractSerializer = CreateSerializer(type);
var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
dataContractSerializer.WriteObject(xmlWriter, responseObject);
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 dataContractSerializer = CreateSerializer(wrappingType);
dataContractSerializer.WriteObject(xmlWriter, obj);
}
return Task.FromResult(true);

View File

@ -30,16 +30,26 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedEncodings = new List<Encoding>();
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WrapperProviderFactories = new List<IWrapperProviderFactory>();
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <inheritdoc />
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for de-serialization.
/// </summary>
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
/// <inheritdoc />
public IList<Encoding> SupportedEncodings { get; private set; }
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
/// <inheritdoc />
public IList<Encoding> SupportedEncodings { get; }
/// <summary>
/// Indicates the acceptable input XML depth.
@ -96,7 +106,10 @@ namespace Microsoft.AspNet.Mvc.Xml
/// <returns>The type to which the XML will be deserialized.</returns>
protected virtual Type GetSerializableType([NotNull] Type declaredType)
{
return SerializableErrorWrapper.CreateSerializableType(declaredType);
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(declaredType, isSerialization: false));
return wrapperProvider?.WrappingType ?? declaredType;
}
/// <summary>
@ -114,7 +127,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the <see cref="XmlSerializer"/>.
/// </summary>
/// <returns>The <see cref="XmlSerializer"/> used during deserialization.</returns>
protected virtual XmlSerializer CreateXmlSerializer(Type type)
protected virtual XmlSerializer CreateSerializer(Type type)
{
return new XmlSerializer(type);
}
@ -136,9 +149,21 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{
var type = GetSerializableType(context.ModelType);
var xmlSerializer = CreateXmlSerializer(type);
var deserializedObject = xmlSerializer.Deserialize(xmlReader);
deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
var serializer = CreateSerializer(type);
var deserializedObject = serializer.Deserialize(xmlReader);
// Unwrap only if the original type was wrapped.
if (type != context.ModelType)
{
var unwrappable = deserializedObject as IUnwrappable;
if (unwrappable != null)
{
deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType);
}
}
return Task.FromResult(deserializedObject);
}
}

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.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml;
@ -38,8 +39,18 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
WrapperProviderFactories = new List<IWrapperProviderFactory>();
WrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(WrapperProviderFactories));
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
/// <summary>
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
/// provide the wrapping type for serialization.
/// </summary>
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
/// <summary>
/// Gets the settings to be used by the XmlWriter.
/// </summary>
@ -48,27 +59,41 @@ namespace Microsoft.AspNet.Mvc.Xml
/// <summary>
/// Gets the type of the object to be serialized.
/// </summary>
/// <param name="declaredType">The declared type.</param>
/// <param name="runtimeType">The runtime type.</param>
/// <returns>The type of the object to be serialized.</returns>
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
/// <param name="declaredType">The declared type of the object.</param>
/// <param name="runtimeType">The runtime type of the object</param>
/// <returns>A type that needs to be serialized.</returns>
protected virtual Type ResolveType(Type declaredType, Type runtimeType)
{
var type = declaredType;
if (declaredType == null || declaredType == typeof(object))
{
if (runtimeType != null)
{
type = runtimeType;
return runtimeType;
}
}
return SerializableErrorWrapper.CreateSerializableType(type);
return declaredType;
}
/// <summary>
/// Gets the type to be serialized.
/// </summary>
/// <param name="type">The original type to be serialized</param>
/// <returns>The original or wrapped type provided by any <see cref="IWrapperProvider"/>.</returns>
protected virtual Type GetSerializableType(Type type)
{
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
new WrapperProviderContext(type, isSerialization: true));
return wrapperProvider?.WrappingType ?? type;
}
/// <inheritdoc />
protected override bool CanWriteType(Type declaredType, Type runtimeType)
{
return CreateSerializer(GetSerializableType(declaredType, runtimeType)) != null;
var type = ResolveType(declaredType, runtimeType);
return CreateSerializer(GetSerializableType(type)) != null;
}
/// <summary>
@ -115,12 +140,26 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var outputStream = new DelegatingStream(innerStream))
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
{
var runtimeType = context.Object == null ? null : context.Object.GetType();
var obj = context.Object;
var runtimeType = obj?.GetType();
var type = GetSerializableType(context.DeclaredType, runtimeType);
var xmlSerializer = CreateSerializer(type);
var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
xmlSerializer.Serialize(xmlWriter, responseObject);
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 = CreateSerializer(wrappingType);
xmlSerializer.Serialize(xmlWriter, obj);
}
return Task.FromResult(true);

View File

@ -14,4 +14,9 @@
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -20,9 +20,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{
private readonly IServiceProvider _provider = TestHelper.CreateServices("ActionResultsWebSite");
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
private const string sampleIntError = "The field SampleInt must be between 10 and 100.";
private const string sampleStringError =
"The field SampleString must be a string or array type with a minimum length of '15'.";
[Fact]
public async Task BadRequestResult_CanBeReturned()
@ -188,43 +185,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync());
}
[Theory]
[InlineData("http://localhost/Home/Index",
"application/json;charset=utf-8",
"{\"test.SampleInt\":[\"" + sampleIntError + "\"]," +
"\"test.SampleString\":" +
"[\"" + sampleStringError + "\"]}")]
[InlineData("http://localhost/Home/Index",
"application/xml;charset=utf-8",
"<Error><test.SampleInt>" + sampleIntError + "</test.SampleInt>" +
"<test.SampleString>" + sampleStringError +
"</test.SampleString></Error>")]
[InlineData("http://localhost/XmlSerializer/GetSerializableError",
"application/xml;charset=utf-8",
"<Error><test.SampleInt>" + sampleIntError + "</test.SampleInt>" +
"<test.SampleString>" + sampleStringError +
"</test.SampleString></Error>")]
public async Task SerializableErrorIsReturnedInExpectedFormat(string url, string outputFormat, string output)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<DummyClass xmlns=\"http://schemas.datacontract.org/2004/07/ActionResultsWebSite\">" +
"<SampleInt>2</SampleInt><SampleString>foo</SampleString></DummyClass>";
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(outputFormat));
request.Content = new StringContent(input, Encoding.UTF8, "application/xml");
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal(output, await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task SerializableError_CanSerializeNormalObjects()
{

View File

@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
@ -42,5 +46,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var actualBody = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, actualBody);
}
[Fact]
public async Task SerializableErrorIsReturnedInExpectedFormat()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<Employee xmlns=\"http://schemas.datacontract.org/2004/07/FormatterWebSite\">" +
"<Id>2</Id><Name>foo</Name></Employee>";
var expectedOutput = "{\"employee.Id\":[\"The field Id must be between 10 and 100." +
"\"],\"employee.Name\":[\"The field Name must be a string or array type with" +
" a minimum length of '15'.\"]}";
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/CreateEmployee");
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
request.Content = new StringContent(input, Encoding.UTF8, "application/xml");
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal(expectedOutput, await response.Content.ReadAsStringAsync());
}
}
}

View File

@ -10,6 +10,12 @@
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AssemblyName>Microsoft.AspNet.Mvc.FunctionalTests</AssemblyName>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<RootNamespace>Microsoft.AspNet.Mvc.FunctionalTests</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>

View File

@ -64,5 +64,32 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var responseData = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedXml, responseData);
}
[Theory]
[InlineData("application/xml-xmlser")]
[InlineData("application/xml-dcs")]
public async Task IsReturnedInExpectedFormat(string acceptHeader)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<Employee xmlns=\"http://schemas.datacontract.org/2004/07/XmlFormattersWebSite.Models\">" +
"<Id>2</Id><Name>foo</Name></Employee>";
var expected = "<Error><employee.Id>The field Id must be between 10 and 100.</employee.Id>" +
"<employee.Name>The field Name must be a string or array type with a minimum " +
"length of '15'.</employee.Name></Error>";
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/CreateEmployee");
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(acceptHeader));
request.Content = new StringContent(input, Encoding.UTF8, "application/xml-dcs");
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
var responseData = await response.Content.ReadAsStringAsync();
Assert.Equal(expected, responseData);
}
}
}

View File

@ -0,0 +1,202 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class XmlDataContractSerializerFormattersWrappingTest
{
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(XmlFormattersWebSite));
private readonly Action<IApplicationBuilder> _app = new XmlFormattersWebSite.Startup().Configure;
[Theory]
[InlineData("http://localhost/IEnumerable/ValueTypes")]
[InlineData("http://localhost/IQueryable/ValueTypes")]
public async Task CanWrite_ValueTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfint xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">" +
"<int>10</int><int>20</int></ArrayOfint>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes")]
public async Task CanWrite_NonWrappedTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfstring xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">" +
"<string>value1</string><string>value2</string></ArrayOfstring>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes_Empty")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes_Empty")]
public async Task CanWrite_NonWrappedTypes_Empty(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfstring xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes_NullInstance")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes_NullInstance")]
public async Task CanWrite_NonWrappedTypes_NullInstance(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfstring i:nil=\"true\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes")]
[InlineData("http://localhost/IQueryable/WrappedTypes")]
public async Task CanWrite_WrappedTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.datacontract.org/2004/07/XmlFormattersWebSite\"><PersonWrapper>" +
"<Age>35</Age><Id>10</Id><Name>Mike</Name></PersonWrapper><PersonWrapper><Age>35</Age><Id>" +
"11</Id><Name>Jimmy</Name></PersonWrapper></ArrayOfPersonWrapper>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes_Empty")]
[InlineData("http://localhost/IQueryable/WrappedTypes_Empty")]
public async Task CanWrite_WrappedTypes_Empty(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.datacontract.org/2004/07/XmlFormattersWebSite\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes_NullInstance")]
[InlineData("http://localhost/IQueryable/WrappedTypes_NullInstance")]
public async Task CanWrite_WrappedTypes_NullInstance(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper i:nil=\"true\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.datacontract.org/2004/07/XmlFormattersWebSite\" />",
result);
}
[Fact]
public async Task CanWrite_IEnumerableOf_SerializableErrors()
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/IEnumerable/SerializableErrors");
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-dcs"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfSerializableErrorWrapper xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns=\"http://schemas.datacontract.org/2004/07/Microsoft.AspNet.Mvc.Xml\"><SerializableErrorWrapper>" +
"<key1>key1-error</key1><key2>key2-error</key2></SerializableErrorWrapper><SerializableErrorWrapper>" +
"<key3>key1-error</key3><key4>key2-error</key4></SerializableErrorWrapper>" +
"</ArrayOfSerializableErrorWrapper>",
result);
}
}
}

View File

@ -0,0 +1,202 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class XmlSerializerFormattersWrappingTest
{
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(XmlFormattersWebSite));
private readonly Action<IApplicationBuilder> _app = new XmlFormattersWebSite.Startup().Configure;
[Theory]
[InlineData("http://localhost/IEnumerable/ValueTypes")]
[InlineData("http://localhost/IQueryable/ValueTypes")]
public async Task CanWrite_ValueTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfInt xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><int>10</int>" +
"<int>20</int></ArrayOfInt>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes")]
public async Task CanWrite_NonWrappedTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfString xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><string>value1</string>" +
"<string>value2</string></ArrayOfString>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes_NullInstance")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes_NullInstance")]
public async Task CanWrite_NonWrappedTypes_NullInstance(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfString xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xsi:nil=\"true\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/NonWrappedTypes_Empty")]
[InlineData("http://localhost/IQueryable/NonWrappedTypes_Empty")]
public async Task CanWrite_NonWrappedTypes_Empty(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfString xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes")]
[InlineData("http://localhost/IQueryable/WrappedTypes")]
public async Task CanWrite_WrappedTypes(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><PersonWrapper><Id>10</Id>" +
"<Name>Mike</Name><Age>35</Age></PersonWrapper><PersonWrapper><Id>11</Id>" +
"<Name>Jimmy</Name><Age>35</Age></PersonWrapper></ArrayOfPersonWrapper>",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes_Empty")]
[InlineData("http://localhost/IQueryable/WrappedTypes_Empty")]
public async Task CanWrite_WrappedTypes_Empty(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" />",
result);
}
[Theory]
[InlineData("http://localhost/IEnumerable/WrappedTypes_NullInstance")]
[InlineData("http://localhost/IQueryable/WrappedTypes_NullInstance")]
public async Task CanWrite_WrappedTypes_NullInstance(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfPersonWrapper xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xsi:nil=\"true\" />",
result);
}
[Fact]
public async Task CanWrite_IEnumerableOf_SerializableErrors()
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/IEnumerable/SerializableErrors");
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/xml-xmlser"));
// Act
var response = await client.SendAsync(request);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Equal("<ArrayOfSerializableErrorWrapper xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><SerializableErrorWrapper><key1>key1-error</key1>" +
"<key2>key2-error</key2></SerializableErrorWrapper><SerializableErrorWrapper><key3>key1-error</key3>" +
"<key4>key2-error</key4></SerializableErrorWrapper></ArrayOfSerializableErrorWrapper>",
result);
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
using System.Collections.Generic;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Xml
{
public class DelegatingEnumerableTest
{
[Fact]
public void CanEnumerateOn_NonWrappableElementTypes()
{
// Arrange
var numbers = new[] { 10, 20 };
var delegatingEnumerable = new DelegatingEnumerable<int, int>(numbers, elementWrapperProvider: null);
// Act and Assert
Assert.Equal(numbers, delegatingEnumerable);
}
[Fact]
public void DoesNotThrowOn_EmptyCollections_NonWrappableElementTypes()
{
// Arrange
var numbers = new int[] { };
var delegatingEnumerable = new DelegatingEnumerable<int, int>(numbers, elementWrapperProvider: null);
// Act and Assert
Assert.Empty(delegatingEnumerable);
}
[Fact]
public void CanEnumerateOn_WrappableElementTypes()
{
// Arrange
var error1 = new SerializableError();
error1.Add("key1", "key1-error");
var error2 = new SerializableError();
error2.Add("key1", "key1-error");
var errors = new[] { error1, error2 };
var delegatingEnumerable = new DelegatingEnumerable<SerializableErrorWrapper, SerializableError>(
errors,
new SerializableErrorWrapperProvider());
// Act and Assert
Assert.Equal(errors.Length, delegatingEnumerable.Count());
for (var i = 0; i < errors.Length; i++)
{
var errorWrapper = delegatingEnumerable.ElementAt(i);
Assert.IsType<SerializableErrorWrapper>(errorWrapper);
Assert.NotNull(errorWrapper);
Assert.Same(errors[i], errorWrapper.SerializableError);
}
}
[Fact]
public void DoesNotThrowOn_EmptyCollections_WrappableElementTypes()
{
// Arrange
var errors = new SerializableError[] { };
var delegatingEnumerable = new DelegatingEnumerable<SerializableErrorWrapper, SerializableError>(
errors, new SerializableErrorWrapperProvider());
// Act and Assert
Assert.Empty(delegatingEnumerable);
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Xml
{
public class DelegatingEnumeratorTest
{
[Fact]
public void DisposeCalled_OnInnerEnumerator()
{
// Arrange
var innerEnumerator = new Mock<IEnumerator<int>>();
innerEnumerator.Setup(innerEnum => innerEnum.Dispose())
.Verifiable();
var delegatingEnumerator = new DelegatingEnumerator<int, int>(
innerEnumerator.Object,
wrapperProvider: null);
// Act
delegatingEnumerator.Dispose();
// Assert
innerEnumerator.Verify();
}
[Fact]
public void MoveNextCalled_OnInnerEnumerator()
{
// Arrange
var innerEnumerator = new Mock<IEnumerator<int>>();
innerEnumerator.Setup(innerEnum => innerEnum.MoveNext())
.Verifiable();
var delegatingEnumerator = new DelegatingEnumerator<int, int>(
innerEnumerator.Object,
wrapperProvider: null);
// Act
var available = delegatingEnumerator.MoveNext();
// Assert
innerEnumerator.Verify();
}
[Fact]
public void ResetCalled_OnInnerEnumerator()
{
// Arrange
var innerEnumerator = new Mock<IEnumerator<int>>();
innerEnumerator.Setup(innerEnum => innerEnum.Reset())
.Verifiable();
var delegatingEnumerator = new DelegatingEnumerator<int, int>(
innerEnumerator.Object,
wrapperProvider: null);
// Act
delegatingEnumerator.Reset();
// Assert
innerEnumerator.Verify();
}
[Fact]
public void CurrentCalled_OnInnerEnumerator()
{
// Arrange
var innerEnumerator = new Mock<IEnumerator<int>>();
innerEnumerator.SetupGet(innerEnum => innerEnum.Current)
.Verifiable();
var delegatingEnumerator = new DelegatingEnumerator<int, int>(
innerEnumerator.Object,
wrapperProvider: null);
// Act
var obj = delegatingEnumerator.Current;
// Assert
innerEnumerator.Verify();
}
}
}

View File

@ -0,0 +1,191 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc;
using Xunit;
namespace Microsoft.AspNet.Mvc.Xml
{
public class EnumerableWrapperProviderFactoryTest
{
public static TheoryData<Type, object, Type> EnumerableOfTInterfaceData
{
get
{
var serializableError = new SerializableError();
serializableError.Add("key1", "key1-error");
return new TheoryData<Type, object, Type>
{
{
typeof(IEnumerable<string>),
new [] { "value1", "value2" },
typeof(DelegatingEnumerable<string, string>)
},
{
typeof(IEnumerable<int>),
new [] { 10, 20 },
typeof(DelegatingEnumerable<int, int>)
},
{
typeof(IEnumerable<Person>),
new [] { new Person() { Id =10, Name = "John" } },
typeof(DelegatingEnumerable<Person, Person>)
},
{
typeof(IEnumerable<SerializableError>),
new [] { serializableError },
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>)
},
};
}
}
[Theory]
[MemberData(nameof(EnumerableOfTInterfaceData))]
public void Creates_WrapperProvider_EnumerableOfTInterface(
Type declaredType,
object objectToBeWrapped,
Type expectedWrappingType)
{
// Arrange
var wrapperProviderFactories = GetWrapperProviderFactories();
var enumerableWrapperProviderFactory = new EnumerableWrapperProviderFactory(wrapperProviderFactories);
var wrapperProviderContext = new WrapperProviderContext(declaredType, isSerialization: true);
// Act
var wrapperProvider = enumerableWrapperProviderFactory.GetProvider(wrapperProviderContext);
// Assert
Assert.NotNull(wrapperProvider);
Assert.Equal(expectedWrappingType, wrapperProvider.WrappingType);
}
public static TheoryData<Type, object, Type> QueryableOfTInterfaceData
{
get
{
var serializableError = new SerializableError();
serializableError.Add("key1", "key1-error");
return new TheoryData<Type, object, Type>
{
{
typeof(IEnumerable<string>),
(new [] { "value1", "value2" }).AsQueryable(),
typeof(DelegatingEnumerable<string, string>)
},
{
typeof(IEnumerable<int>),
(new [] { 10, 20 }).AsQueryable(),
typeof(DelegatingEnumerable<int, int>)
},
{
typeof(IEnumerable<Person>),
(new [] { new Person() { Id =10, Name = "John" } }).AsQueryable(),
typeof(DelegatingEnumerable<Person, Person>)
},
{
typeof(IEnumerable<SerializableError>),
(new [] { serializableError }).AsQueryable(),
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>)
},
};
}
}
[Theory]
[MemberData(nameof(QueryableOfTInterfaceData))]
public void Creates_WrapperProvider_QueryableOfTInterface(
Type declaredType,
object objectToBeWrapped,
Type expectedWrappingType)
{
// Arrange
var wrapperProviderFactories = GetWrapperProviderFactories();
var enumerableWrapperProviderFactory = new EnumerableWrapperProviderFactory(wrapperProviderFactories);
var wrapperProviderContext = new WrapperProviderContext(declaredType, isSerialization: true);
// Act
var wrapperProvider = enumerableWrapperProviderFactory.GetProvider(wrapperProviderContext);
// Assert
Assert.NotNull(wrapperProvider);
Assert.Equal(expectedWrappingType, wrapperProvider.WrappingType);
}
public static TheoryData<Type, object> ConcreteEnumerableOfTData
{
get
{
var serializableError = new SerializableError();
serializableError.Add("key1", "key1-error");
return new TheoryData<Type, object>
{
{
typeof(string), // 'string' implements IEnumerable<char>
"value"
},
{
typeof(List<int>),
(new [] { 10, 20 }).ToList()
},
{
typeof(List<Person>),
(new [] { new Person() { Id =10, Name = "John" } }).ToList()
},
{
typeof(List<SerializableError>),
(new [] { serializableError }).ToList()
},
{
typeof(PersonList),
new PersonList()
},
};
}
}
[Theory]
[MemberData(nameof(ConcreteEnumerableOfTData))]
public void DoesNot_CreateWrapperProvider_ForConcrete_EnumerableOfTImplementations(
Type declaredType,
object objectToBeWrapped)
{
// Arrange
var wrapperProviderFactories = GetWrapperProviderFactories();
var enumerableWrapperProviderFactory = new EnumerableWrapperProviderFactory(wrapperProviderFactories);
var wrapperProviderContext = new WrapperProviderContext(declaredType, isSerialization: true);
// Act
var wrapperProvider = enumerableWrapperProviderFactory.GetProvider(wrapperProviderContext);
// Assert
Assert.Null(wrapperProvider);
}
private IEnumerable<IWrapperProviderFactory> GetWrapperProviderFactories()
{
var wrapperProviderFactories = new List<IWrapperProviderFactory>();
wrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(wrapperProviderFactories));
wrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
return wrapperProviderFactories;
}
internal class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
internal class PersonList : List<Person>
{
}
}
}

View File

@ -0,0 +1,96 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.AspNet.Mvc;
using Xunit;
namespace Microsoft.AspNet.Mvc.Xml
{
public class EnumerableWrapperProviderTest
{
[Theory]
[InlineData(typeof(IEnumerable<SerializableError>),
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>))]
[InlineData(typeof(IQueryable<SerializableError>),
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>))]
[InlineData(typeof(ICollection<SerializableError>),
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>))]
[InlineData(typeof(IList<SerializableError>),
typeof(DelegatingEnumerable<SerializableErrorWrapper, SerializableError>))]
public void Gets_DelegatingWrappingType(Type declaredEnumerableOfT, Type expectedType)
{
// Arrange
var wrapperProvider = new EnumerableWrapperProvider(
declaredEnumerableOfT,
new SerializableErrorWrapperProvider());
// Act
var wrappingType = wrapperProvider.WrappingType;
// Assert
Assert.NotNull(wrappingType);
Assert.Equal(expectedType, wrappingType);
}
[Fact]
public void Wraps_EmptyCollections()
{
// Arrange
var declaredEnumerableOfT = typeof(IEnumerable<int>);
var wrapperProvider = new EnumerableWrapperProvider(
declaredEnumerableOfT,
elementWrapperProvider: null);
// Act
var wrapped = wrapperProvider.Wrap(new int[] { });
// Assert
Assert.Equal(typeof(DelegatingEnumerable<int, int>), wrapperProvider.WrappingType);
Assert.NotNull(wrapped);
var delegatingEnumerable = wrapped as DelegatingEnumerable<int, int>;
Assert.NotNull(delegatingEnumerable);
Assert.Equal(0, delegatingEnumerable.Count());
}
[Fact]
public void Ignores_NullInstances()
{
// Arrange
var declaredEnumerableOfT = typeof(IEnumerable<int>);
var wrapperProvider = new EnumerableWrapperProvider(
declaredEnumerableOfT,
elementWrapperProvider: null);
// Act
var wrapped = wrapperProvider.Wrap(null);
// Assert
Assert.Equal(typeof(DelegatingEnumerable<int, int>), wrapperProvider.WrappingType);
Assert.Null(wrapped);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(List<int>))]
[InlineData(typeof(List<Person>))]
[InlineData(typeof(List<SerializableError>))]
[InlineData(typeof(PersonList))]
public void ThrowsArugmentExceptionFor_ConcreteEnumerableOfT(Type declaredType)
{
// Arrange
var expectedMessage = "The type must be an interface and must be or derive from 'IEnumerable`1'." +
"\r\nParameter name: sourceEnumerableOfT";
// Act and Assert
var ex = Assert.Throws<ArgumentException>(() => new EnumerableWrapperProvider(
declaredType,
elementWrapperProvider: null));
Assert.Equal(expectedMessage, ex.Message);
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.Xml
{
public class PersonList : List<Person>
{
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
public class PersonWrapper : IUnwrappable
{
public PersonWrapper() { }
public PersonWrapper(Person person)
{
Id = person.Id;
Name = person.Name;
Age = 35;
}
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("{0}, {1}, {2}", Id, Name, Age);
}
public object Unwrap(Type declaredType)
{
return new Person() { Id = this.Id, Name = this.Name };
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
public class PersonWrapperProvider : IWrapperProvider
{
public object Wrap(object obj)
{
var person = obj as Person;
if (person == null)
{
return obj;
}
return new PersonWrapper(person);
}
public Type WrappingType
{
get
{
return typeof(PersonWrapper);
}
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.Xml
{
public class PersonWrapperProviderFactory : IWrapperProviderFactory
{
public IWrapperProvider GetProvider(WrapperProviderContext context)
{
if (context.DeclaredType == typeof(Person))
{
return new PersonWrapperProvider();
}
return null;
}
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
using Microsoft.AspNet.Mvc;
namespace Microsoft.AspNet.Mvc.Xml
{
public class SerializableErrorWrapperProviderTest
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Gets_SerializableErrorWrapper_AsWrappingType(bool isSerialization)
{
// Arrange
var wrapperProvider = new SerializableErrorWrapperProvider();
// Act and Assert
Assert.Equal(typeof(SerializableErrorWrapper), wrapperProvider.WrappingType);
}
[Fact]
public void Wraps_SerializableErrorInstance()
{
// Arrange
var wrapperProvider = new SerializableErrorWrapperProvider();
var serializableError = new SerializableError();
// Act
var wrapped = wrapperProvider.Wrap(serializableError);
// Assert
Assert.NotNull(wrapped);
var errorWrapper = wrapped as SerializableErrorWrapper;
Assert.NotNull(errorWrapper);
Assert.Same(serializableError, errorWrapper.SerializableError);
}
[Fact]
public void ThrowsExceptionOn_NonSerializableErrorInstances()
{
// Arrange
var wrapperProvider = new SerializableErrorWrapperProvider();
var person = new Person() { Id = 10, Name = "John" };
var expectedMessage = string.Format("The object to be wrapped must be of type '{0}'" +
" but was of type 'Person'.\r\nParameter name: original",
typeof(SerializableErrorWrapper).Name);
// Act and Assert
var exception = Assert.Throws<ArgumentException>(() => wrapperProvider.Wrap(person));
Assert.Equal(expectedMessage, exception.Message);
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
namespace Microsoft.AspNet.Mvc.Xml
{
public class SerializableWrapperProviderFactoryTest
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Creates_WrapperProvider_ForSerializableErrorType(bool isSerialization)
{
// Arrange
var serializableErroWrapperProviderFactory = new SerializableErrorWrapperProviderFactory();
// Act
var wrapperProvider = serializableErroWrapperProviderFactory.GetProvider(
new WrapperProviderContext(typeof(SerializableError), isSerialization));
// Assert
Assert.NotNull(wrapperProvider);
Assert.Equal(typeof(SerializableErrorWrapper), wrapperProvider.WrappingType);
}
[Fact]
public void ReturnsNullFor_NonSerializableErrorTypes()
{
// Arrange
var serializableErroWrapperProviderFactory = new SerializableErrorWrapperProviderFactory();
// Act
var wrapperProvider = serializableErroWrapperProviderFactory.GetProvider(
new WrapperProviderContext(typeof(Person), isSerialization: true));
// Assert
Assert.Null(wrapperProvider);
}
}
}

View File

@ -15,7 +15,7 @@ using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlDataContractSerializerInputFormatterTest
{
@ -345,26 +345,6 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expectedString, levelOneModel.sampleString);
}
[Fact]
public async Task ReadAsync_ReadsSerializableErrorXml()
{
// Arrange
var serializableErrorXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Error><key1>Test Error 1 Test Error 2</key1><key2>Test Error 3</key2></Error>";
var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(serializableErrorXml);
var context = GetInputFormatterContext(contentBytes, typeof(SerializableError));
// Act
var model = await formatter.ReadAsync(context);
// Assert
var serializableError = model as SerializableError;
Assert.NotNull(serializableError);
Assert.Equal("Test Error 1 Test Error 2", serializableError["key1"]);
Assert.Equal("Test Error 3", serializableError["key2"]);
}
[Fact]
public async Task ReadAsync_ThrowsWhenNotConfiguredWithRootName()
{

View File

@ -15,7 +15,7 @@ using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlDataContractSerializerOutputFormatterTest
{
@ -134,7 +134,7 @@ namespace Microsoft.AspNet.Mvc
var sampleInput = new DummyClass { SampleInt = 10 };
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
var formatter = new XmlDataContractSerializerOutputFormatter(writerSettings);
var expectedOutput = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
var expectedOutput = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>";
@ -222,7 +222,7 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>",
new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body,
new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body,
Encoding.UTF8).ReadToEnd());
}
@ -348,7 +348,7 @@ namespace Microsoft.AspNet.Mvc
[Theory]
[MemberData(nameof(TypesForGetSupportedContentTypes))]
public void GetSupportedContentTypes_ReturnsSupportedTypes(Type declaredType,
public void GetSupportedContentTypes_ReturnsSupportedTypes(Type declaredType,
Type runtimeType, object expectedOutput)
{
// Arrange
@ -378,7 +378,7 @@ namespace Microsoft.AspNet.Mvc
var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass));
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException),
await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}
@ -394,7 +394,7 @@ namespace Microsoft.AspNet.Mvc
var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType());
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException),
await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}

View File

@ -15,7 +15,7 @@ using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlSerializerInputFormatterTest
{
@ -346,26 +346,6 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), levelOneModel.SampleDate);
}
[Fact]
public async Task ReadAsync_ReadsSerializableErrorXml()
{
// Arrange
var serializableErrorXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<Error><key1>Test Error 1 Test Error 2</key1><key2>Test Error 3</key2></Error>";
var formatter = new XmlSerializerInputFormatter();
var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(serializableErrorXml);
var context = GetInputFormatterContext(contentBytes, typeof(SerializableError));
// Act
var model = await formatter.ReadAsync(context);
// Assert
var serializableError = model as SerializableError;
Assert.NotNull(serializableError);
Assert.Equal("Test Error 1 Test Error 2", serializableError["key1"]);
Assert.Equal("Test Error 3", serializableError["key2"]);
}
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var actionContext = GetActionContext(contentBytes);

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
@ -13,7 +14,7 @@ using Microsoft.AspNet.Mvc.Xml;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlSerializerOutputFormatterTest
{
@ -243,7 +244,7 @@ namespace Microsoft.AspNet.Mvc
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
}
public static IEnumerable<object[]> TypesForCanWriteResult
{
get
@ -257,6 +258,10 @@ namespace Microsoft.AspNet.Mvc
new Dictionary<string, string> { { "Hello", "world" } }, typeof(object), false };
yield return new object[] {
new Dictionary<string, string> { { "Hello", "world" } }, typeof(Dictionary<string,string>), false };
yield return new object[] {
new[] {"value1", "value2"}, typeof(IEnumerable<string>), true };
yield return new object[] {
Enumerable.Range(1, 2).Select(i => "value" + i).AsQueryable(), typeof(IQueryable<string>), true };
}
}

View File

@ -15,4 +15,9 @@
<DevelopmentServerPort>41642</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc;
namespace FormatterWebSite.Controllers
{
public class SerializableErrorController : Controller
{
[HttpPost]
public IActionResult CreateEmployee([FromBody] Employee employee)
{
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
return Content("Hello World!");
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
namespace FormatterWebSite
{
public class Employee
{
[Range(10, 100)]
public int Id { get; set; }
[MinLength(15)]
public string Name { get; set; }
}
}

View File

@ -1 +1 @@
index:@GetType().GetTypeInfo().Assembly.GetName()
index:@GetType().GetTypeInfo().Assembly.GetName()

View File

@ -1,2 +1,2 @@
Layout:@GetType().GetTypeInfo().Assembly.FullName
@RenderBody()
@RenderBody()

View File

@ -1,4 +1,4 @@
@using System.Reflection
@{ Layout = "/views/Home/Layout.cshtml";}
_viewstart:@GetType().GetTypeInfo().Assembly.FullName

View File

@ -1,6 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;
namespace XmlFormattersWebSite

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;
using XmlFormattersWebSite.Models;
namespace XmlFormattersWebSite.Controllers
{
public class IEnumerableController : Controller
{
public IEnumerable<int> ValueTypes()
{
return new[] { 10, 20 };
}
public IEnumerable<string> NonWrappedTypes()
{
return new[] { "value1", "value2" };
}
public IEnumerable<string> NonWrappedTypes_Empty()
{
return new string[] { };
}
public IEnumerable<Person> WrappedTypes()
{
return new[] {
new Person() { Id = 10, Name = "Mike" },
new Person() { Id = 11, Name = "Jimmy" }
};
}
public IEnumerable<Person> WrappedTypes_Empty()
{
return new Person[] { };
}
public IEnumerable<string> NonWrappedTypes_NullInstance()
{
return null;
}
public IEnumerable<Person> WrappedTypes_NullInstance()
{
return null;
}
public IEnumerable<SerializableError> SerializableErrors()
{
List<SerializableError> errors = new List<SerializableError>();
var error1 = new SerializableError();
error1.Add("key1", "key1-error");
error1.Add("key2", "key2-error");
var error2 = new SerializableError();
error2.Add("key3", "key1-error");
error2.Add("key4", "key2-error");
errors.Add(error1);
errors.Add(error2);
return errors;
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;
using XmlFormattersWebSite.Models;
namespace XmlFormattersWebSite.Controllers
{
public class IQueryableController : Controller
{
public IQueryable<int> ValueTypes()
{
return Enumerable.Range(1, 2).Select(i => i * 10).AsQueryable();
}
public IQueryable<string> NonWrappedTypes()
{
return Enumerable.Range(1, 2).Select(i => "value" + i).AsQueryable();
}
public IQueryable<Person> WrappedTypes()
{
return new[] {
new Person() { Id = 10, Name = "Mike" },
new Person() { Id = 11, Name = "Jimmy" }
}.AsQueryable();
}
public IQueryable<Person> WrappedTypes_Empty()
{
return (new Person[] { }).AsQueryable();
}
public IQueryable<string> NonWrappedTypes_Empty()
{
return (new string[] { }).AsQueryable();
}
public IQueryable<string> NonWrappedTypes_NullInstance()
{
return null;
}
public IQueryable<Person> WrappedTypes_NullInstance()
{
return null;
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNet.Mvc;
using XmlFormattersWebSite.Models;
namespace XmlFormattersWebSite.Controllers
{
@ -33,5 +34,16 @@ namespace XmlFormattersWebSite.Controllers
{
return serializableError;
}
[HttpPost]
public IActionResult CreateEmployee([FromBody] Employee employee)
{
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
return Content("Hello World!");
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
namespace XmlFormattersWebSite.Models
{
public class Employee
{
[Range(10, 100)]
public int Id { get; set; }
[MinLength(15)]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace XmlFormattersWebSite.Models
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using XmlFormattersWebSite.Models;
using Microsoft.AspNet.Mvc.Xml;
namespace XmlFormattersWebSite
{
public class PersonWrapper : IUnwrappable
{
public PersonWrapper() { }
public PersonWrapper(Person person)
{
Id = person.Id;
Name = person.Name;
Age = 35;
}
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("{0}, {1}, {2}", Id, Name, Age);
}
public object Unwrap(Type declaredType)
{
return new Person() { Id = this.Id, Name = this.Name };
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.Xml;
using XmlFormattersWebSite.Models;
namespace XmlFormattersWebSite
{
public class PersonWrapperProvider : IWrapperProvider
{
public object Wrap(object obj)
{
var person = obj as Person;
if (person == null)
{
return obj;
}
return new PersonWrapper(person);
}
public Type WrappingType
{
get
{
return typeof(PersonWrapper);
}
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.Xml;
using XmlFormattersWebSite.Models;
namespace XmlFormattersWebSite
{
public class PersonWrapperProviderFactory : IWrapperProviderFactory
{
public IWrapperProvider GetProvider(WrapperProviderContext context)
{
if (context.DeclaredType == typeof(Person))
{
return new PersonWrapperProvider();
}
return null;
}
}
}

View File

@ -61,6 +61,11 @@ namespace XmlFormattersWebSite
options.InputFormatters.Add(xmlSerializerInputFormatter);
options.OutputFormatters.Add(dcsOutputFormatter);
options.OutputFormatters.Add(xmlSerializerOutputFormatter);
xmlSerializerInputFormatter.WrapperProviderFactories.Add(new PersonWrapperProviderFactory());
xmlSerializerOutputFormatter.WrapperProviderFactories.Add(new PersonWrapperProviderFactory());
dcsInputFormatter.WrapperProviderFactories.Add(new PersonWrapperProviderFactory());
dcsOutputFormatter.WrapperProviderFactories.Add(new PersonWrapperProviderFactory());
});
});
@ -71,7 +76,6 @@ namespace XmlFormattersWebSite
{
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
});
}
}

View File

@ -14,7 +14,11 @@
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
"aspnetcore50": {
"dependencies": {
"System.Linq.Queryable": "4.0.0-beta-*"
}
}
},
"webroot": "wwwroot"
}