[Fixes #1121,#1508] Wrap objects for serialization/deserialization and DelegatingEnumerable<> support for IEnumerable<T> and IQueryable<T>
This commit is contained in:
parent
7c7eaa264d
commit
e5176d22f6
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
index:@GetType().GetTypeInfo().Assembly.GetName()
|
||||
index:@GetType().GetTypeInfo().Assembly.GetName()
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
Layout:@GetType().GetTypeInfo().Assembly.FullName
|
||||
@RenderBody()
|
||||
@RenderBody()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@using System.Reflection
|
||||
@{ Layout = "/views/Home/Layout.cshtml";}
|
||||
_viewstart:@GetType().GetTypeInfo().Assembly.FullName
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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" });
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@
|
|||
},
|
||||
"frameworks": {
|
||||
"aspnet50": { },
|
||||
"aspnetcore50": { }
|
||||
"aspnetcore50": {
|
||||
"dependencies": {
|
||||
"System.Linq.Queryable": "4.0.0-beta-*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"webroot": "wwwroot"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue