[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;
|
||||||
using System.Xml.Schema;
|
using System.Xml.Schema;
|
||||||
using System.Xml.Serialization;
|
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.
|
/// Wrapper class for <see cref="SerializableError"/> to enable it to be serialized by the xml formatters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[XmlRoot("Error")]
|
[XmlRoot("Error")]
|
||||||
public sealed class SerializableErrorWrapper : IXmlSerializable
|
public sealed class SerializableErrorWrapper : IXmlSerializable, IUnwrappable
|
||||||
{
|
{
|
||||||
// Note: XmlSerializer requires to have default constructor
|
// Note: XmlSerializer requires to have default constructor
|
||||||
public SerializableErrorWrapper()
|
public SerializableErrorWrapper()
|
||||||
|
|
@ -83,61 +86,10 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Gets the
|
public object Unwrap([NotNull] Type declaredType)
|
||||||
/// </summary>
|
|
||||||
/// <param name="modelType"></param>
|
|
||||||
/// <param name="deserializedObject"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static object UnwrapSerializableErrorObject([NotNull] Type modelType, object deserializedObject)
|
|
||||||
{
|
{
|
||||||
// Since we expect users to typically bind with SerializableError type,
|
return SerializableError;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 = new List<Encoding>();
|
||||||
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
||||||
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
||||||
|
|
||||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||||
|
|
||||||
_serializerSettings = new DataContractSerializerSettings();
|
_serializerSettings = new DataContractSerializerSettings();
|
||||||
|
|
||||||
|
WrapperProviderFactories = new List<IWrapperProviderFactory>();
|
||||||
|
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
|
||||||
|
/// provide the wrapping type for de-serialization.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IList<Encoding> SupportedEncodings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the acceptable input XML depth.
|
/// 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>
|
/// <returns>The type to which the XML will be deserialized.</returns>
|
||||||
protected virtual Type GetSerializableType([NotNull] Type declaredType)
|
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>
|
/// <summary>
|
||||||
|
|
@ -157,9 +171,21 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
|
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
|
||||||
{
|
{
|
||||||
var type = GetSerializableType(context.ModelType);
|
var type = GetSerializableType(context.ModelType);
|
||||||
var dataContractSerializer = CreateSerializer(type);
|
|
||||||
var deserializedObject = dataContractSerializer.ReadObject(xmlReader);
|
var serializer = CreateSerializer(type);
|
||||||
deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
|
|
||||||
|
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);
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -40,9 +41,20 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||||
|
|
||||||
WriterSettings = writerSettings;
|
WriterSettings = writerSettings;
|
||||||
|
|
||||||
_serializerSettings = new DataContractSerializerSettings();
|
_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>
|
/// <summary>
|
||||||
/// Gets the settings to be used by the XmlWriter.
|
/// Gets the settings to be used by the XmlWriter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -72,24 +84,38 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
/// <param name="declaredType">The declared type.</param>
|
/// <param name="declaredType">The declared type.</param>
|
||||||
/// <param name="runtimeType">The runtime type.</param>
|
/// <param name="runtimeType">The runtime type.</param>
|
||||||
/// <returns>The type of the object to be serialized.</returns>
|
/// <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 (declaredType == null || declaredType == typeof(object))
|
||||||
{
|
{
|
||||||
if (runtimeType != null)
|
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 />
|
/// <inheritdoc />
|
||||||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
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>
|
/// <summary>
|
||||||
|
|
@ -138,12 +164,26 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
using (var outputStream = new DelegatingStream(innerStream))
|
using (var outputStream = new DelegatingStream(innerStream))
|
||||||
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
|
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 resolvedType = ResolveType(context.DeclaredType, runtimeType);
|
||||||
var dataContractSerializer = CreateSerializer(type);
|
|
||||||
var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
|
var wrappingType = GetSerializableType(resolvedType);
|
||||||
dataContractSerializer.WriteObject(xmlWriter, responseObject);
|
|
||||||
|
// 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);
|
return Task.FromResult(true);
|
||||||
|
|
@ -30,16 +30,26 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
SupportedEncodings = new List<Encoding>();
|
SupportedEncodings = new List<Encoding>();
|
||||||
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
||||||
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
||||||
|
|
||||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||||
|
|
||||||
|
WrapperProviderFactories = new List<IWrapperProviderFactory>();
|
||||||
|
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
/// Gets the list of <see cref="IWrapperProviderFactory"/> to
|
||||||
|
/// provide the wrapping type for de-serialization.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IWrapperProviderFactory> WrapperProviderFactories { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IList<Encoding> SupportedEncodings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the acceptable input XML depth.
|
/// 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>
|
/// <returns>The type to which the XML will be deserialized.</returns>
|
||||||
protected virtual Type GetSerializableType([NotNull] Type declaredType)
|
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>
|
/// <summary>
|
||||||
|
|
@ -114,7 +127,7 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
/// Called during deserialization to get the <see cref="XmlSerializer"/>.
|
/// Called during deserialization to get the <see cref="XmlSerializer"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="XmlSerializer"/> used during deserialization.</returns>
|
/// <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);
|
return new XmlSerializer(type);
|
||||||
}
|
}
|
||||||
|
|
@ -136,9 +149,21 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
|
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
|
||||||
{
|
{
|
||||||
var type = GetSerializableType(context.ModelType);
|
var type = GetSerializableType(context.ModelType);
|
||||||
var xmlSerializer = CreateXmlSerializer(type);
|
|
||||||
var deserializedObject = xmlSerializer.Deserialize(xmlReader);
|
var serializer = CreateSerializer(type);
|
||||||
deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
|
|
||||||
|
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);
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
@ -38,8 +39,18 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||||
|
|
||||||
WriterSettings = writerSettings;
|
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>
|
/// <summary>
|
||||||
/// Gets the settings to be used by the XmlWriter.
|
/// Gets the settings to be used by the XmlWriter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -48,27 +59,41 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the object to be serialized.
|
/// Gets the type of the object to be serialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="declaredType">The declared type.</param>
|
/// <param name="declaredType">The declared type of the object.</param>
|
||||||
/// <param name="runtimeType">The runtime type.</param>
|
/// <param name="runtimeType">The runtime type of the object</param>
|
||||||
/// <returns>The type of the object to be serialized.</returns>
|
/// <returns>A type that needs to be serialized.</returns>
|
||||||
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
|
protected virtual Type ResolveType(Type declaredType, Type runtimeType)
|
||||||
{
|
{
|
||||||
var type = declaredType;
|
|
||||||
if (declaredType == null || declaredType == typeof(object))
|
if (declaredType == null || declaredType == typeof(object))
|
||||||
{
|
{
|
||||||
if (runtimeType != null)
|
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 />
|
/// <inheritdoc />
|
||||||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
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>
|
/// <summary>
|
||||||
|
|
@ -115,12 +140,26 @@ namespace Microsoft.AspNet.Mvc.Xml
|
||||||
using (var outputStream = new DelegatingStream(innerStream))
|
using (var outputStream = new DelegatingStream(innerStream))
|
||||||
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
|
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 resolvedType = ResolveType(context.DeclaredType, runtimeType);
|
||||||
var xmlSerializer = CreateSerializer(type);
|
|
||||||
var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
|
var wrappingType = GetSerializableType(resolvedType);
|
||||||
xmlSerializer.Serialize(xmlWriter, responseObject);
|
|
||||||
|
// 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);
|
return Task.FromResult(true);
|
||||||
|
|
@ -14,4 +14,9 @@
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<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>
|
</Project>
|
||||||
|
|
@ -20,9 +20,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _provider = TestHelper.CreateServices("ActionResultsWebSite");
|
private readonly IServiceProvider _provider = TestHelper.CreateServices("ActionResultsWebSite");
|
||||||
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
|
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]
|
[Fact]
|
||||||
public async Task BadRequestResult_CanBeReturned()
|
public async Task BadRequestResult_CanBeReturned()
|
||||||
|
|
@ -188,43 +185,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync());
|
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]
|
[Fact]
|
||||||
public async Task SerializableError_CanSerializeNormalObjects()
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
|
|
@ -42,5 +46,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var actualBody = await response.Content.ReadAsStringAsync();
|
var actualBody = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(expectedBody, actualBody);
|
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>
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
</PropertyGroup>
|
</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>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -64,5 +64,32 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var responseData = await response.Content.ReadAsStringAsync();
|
var responseData = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(expectedXml, responseData);
|
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 Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc.Xml
|
||||||
{
|
{
|
||||||
public class XmlDataContractSerializerInputFormatterTest
|
public class XmlDataContractSerializerInputFormatterTest
|
||||||
{
|
{
|
||||||
|
|
@ -345,26 +345,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Assert.Equal(expectedString, levelOneModel.sampleString);
|
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]
|
[Fact]
|
||||||
public async Task ReadAsync_ThrowsWhenNotConfiguredWithRootName()
|
public async Task ReadAsync_ThrowsWhenNotConfiguredWithRootName()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ using Microsoft.Net.Http.Headers;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc.Xml
|
||||||
{
|
{
|
||||||
public class XmlDataContractSerializerOutputFormatterTest
|
public class XmlDataContractSerializerOutputFormatterTest
|
||||||
{
|
{
|
||||||
|
|
@ -134,7 +134,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
var sampleInput = new DummyClass { SampleInt = 10 };
|
var sampleInput = new DummyClass { SampleInt = 10 };
|
||||||
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
|
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
|
||||||
var formatter = new XmlDataContractSerializerOutputFormatter(writerSettings);
|
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\">" +
|
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
|
||||||
"<SampleInt>10</SampleInt></DummyClass>";
|
"<SampleInt>10</SampleInt></DummyClass>";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ using Microsoft.AspNet.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc.Xml
|
||||||
{
|
{
|
||||||
public class XmlSerializerInputFormatterTest
|
public class XmlSerializerInputFormatterTest
|
||||||
{
|
{
|
||||||
|
|
@ -346,26 +346,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), levelOneModel.SampleDate);
|
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)
|
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
|
||||||
{
|
{
|
||||||
var actionContext = GetActionContext(contentBytes);
|
var actionContext = GetActionContext(contentBytes);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -13,7 +14,7 @@ using Microsoft.AspNet.Mvc.Xml;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc.Xml
|
||||||
{
|
{
|
||||||
public class XmlSerializerOutputFormatterTest
|
public class XmlSerializerOutputFormatterTest
|
||||||
{
|
{
|
||||||
|
|
@ -257,6 +258,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
new Dictionary<string, string> { { "Hello", "world" } }, typeof(object), false };
|
new Dictionary<string, string> { { "Hello", "world" } }, typeof(object), false };
|
||||||
yield return new object[] {
|
yield return new object[] {
|
||||||
new Dictionary<string, string> { { "Hello", "world" } }, typeof(Dictionary<string,string>), false };
|
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>
|
<DevelopmentServerPort>41642</DevelopmentServerPort>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<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>
|
</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,6 +1,9 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// 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 Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
namespace XmlFormattersWebSite
|
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 System;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using XmlFormattersWebSite.Models;
|
||||||
|
|
||||||
namespace XmlFormattersWebSite.Controllers
|
namespace XmlFormattersWebSite.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -33,5 +34,16 @@ namespace XmlFormattersWebSite.Controllers
|
||||||
{
|
{
|
||||||
return serializableError;
|
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.InputFormatters.Add(xmlSerializerInputFormatter);
|
||||||
options.OutputFormatters.Add(dcsOutputFormatter);
|
options.OutputFormatters.Add(dcsOutputFormatter);
|
||||||
options.OutputFormatters.Add(xmlSerializerOutputFormatter);
|
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}",
|
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||||
defaults: new { controller = "Home", action = "Index" });
|
defaults: new { controller = "Home", action = "Index" });
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"aspnet50": { },
|
"aspnet50": { },
|
||||||
"aspnetcore50": { }
|
"aspnetcore50": {
|
||||||
|
"dependencies": {
|
||||||
|
"System.Linq.Queryable": "4.0.0-beta-*"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"webroot": "wwwroot"
|
"webroot": "wwwroot"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue