diff --git a/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerable.cs b/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerable.cs
new file mode 100644
index 0000000000..3536863afc
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerable.cs
@@ -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
+{
+ ///
+ /// Serializes types by delegating them through a concrete implementation.
+ ///
+ /// The wrapping or original type of the
+ /// to proxy.
+ /// The type parameter of the original
+ /// to proxy.
+ public class DelegatingEnumerable : IEnumerable
+ {
+ private readonly IEnumerable _source;
+ private readonly IWrapperProvider _wrapperProvider;
+
+ ///
+ /// Initializes a .
+ ///
+ ///
+ /// This constructor is necessary for
+ /// to serialize.
+ ///
+ public DelegatingEnumerable()
+ {
+ _source = Enumerable.Empty();
+ }
+
+ ///
+ /// Initializes a with the original
+ /// and the wrapper provider for wrapping individual elements.
+ ///
+ /// The instance to get the enumerator from.
+ /// The wrapper provider for wrapping individual elements.
+ public DelegatingEnumerable([NotNull] IEnumerable source, IWrapperProvider elementWrapperProvider)
+ {
+ _source = source;
+ _wrapperProvider = elementWrapperProvider;
+ }
+
+ ///
+ /// Gets a delegating enumerator of the original source which is being
+ /// wrapped.
+ ///
+ /// The delegating enumerator of the original source.
+ public IEnumerator GetEnumerator()
+ {
+ return new DelegatingEnumerator(_source.GetEnumerator(), _wrapperProvider);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The item to add. Unused.
+ public void Add(object item)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Gets a delegating enumerator of the original source which is being
+ /// wrapped.
+ ///
+ /// The delegating enumerator of the original source.
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerator.cs b/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerator.cs
new file mode 100644
index 0000000000..937679a643
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/DelegatingEnumerator.cs
@@ -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
+{
+ ///
+ /// Delegates enumeration of elements to the original enumerator and wraps the items
+ /// with the supplied .
+ ///
+ /// The type to which the individual elements need to be wrapped to.
+ /// The original type of the element being wrapped.
+ public class DelegatingEnumerator : IEnumerator
+ {
+ private readonly IEnumerator _inner;
+ private readonly IWrapperProvider _wrapperProvider;
+
+ ///
+ /// Initializes a which enumerates
+ /// over the elements of the original enumerator and wraps them using the supplied
+ /// .
+ ///
+ /// The original enumerator.
+ /// The wrapper provider to wrap individual elements.
+ public DelegatingEnumerator([NotNull] IEnumerator inner, IWrapperProvider wrapperProvider)
+ {
+ _inner = inner;
+ _wrapperProvider = wrapperProvider;
+ }
+
+ ///
+ 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);
+ }
+ }
+
+ ///
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ _inner.Dispose();
+ }
+
+ ///
+ public bool MoveNext()
+ {
+ return _inner.MoveNext();
+ }
+
+ ///
+ public void Reset()
+ {
+ _inner.Reset();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProvider.cs b/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProvider.cs
new file mode 100644
index 0000000000..a86ba11939
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProvider.cs
@@ -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
+{
+ ///
+ /// Provides a for interface types which implement
+ /// .
+ ///
+ public class EnumerableWrapperProvider : IWrapperProvider
+ {
+ private readonly IWrapperProvider _wrapperProvider;
+ private readonly ConstructorInfo _wrappingTypeConstructor;
+
+ ///
+ /// Initializes an instance of .
+ ///
+ /// Type of the original
+ /// that is being wrapped.
+ /// The for the element type.
+ /// Can be null.
+ 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)
+ });
+ }
+
+ ///
+ public Type WrappingType
+ {
+ get;
+ }
+
+ ///
+ public object Wrap(object original)
+ {
+ if (original == null)
+ {
+ return null;
+ }
+
+ return _wrappingTypeConstructor.Invoke(new object[] { original, _wrapperProvider });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProviderFactory.cs
new file mode 100644
index 0000000000..09a720b853
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/EnumerableWrapperProviderFactory.cs
@@ -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
+{
+ ///
+ /// Creates an for interface types implementing the
+ /// type.
+ ///
+ public class EnumerableWrapperProviderFactory : IWrapperProviderFactory
+ {
+ private readonly IEnumerable _wrapperProviderFactories;
+
+ ///
+ /// Initializes an with a list
+ /// .
+ ///
+ /// List of .
+ public EnumerableWrapperProviderFactory([NotNull] IEnumerable wrapperProviderFactories)
+ {
+ _wrapperProviderFactories = wrapperProviderFactories;
+ }
+
+ ///
+ /// Gets an for the provided context.
+ ///
+ /// The .
+ /// An instance of if the declared type is
+ /// an interface and implements .
+ public IWrapperProvider GetProvider([NotNull] WrapperProviderContext context)
+ {
+ if (context.IsSerialization)
+ {
+ // Example: IEnumerable
+ var declaredType = context.DeclaredType;
+
+ // We only wrap interfaces types(ex: IEnumerable, IQueryable, IList etc.) and not
+ // concrete types like List, Collection which implement IEnumerable.
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Formatters/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.Xml/FormattingUtilities.cs
similarity index 100%
rename from src/Microsoft.AspNet.Mvc.Xml/Formatters/FormattingUtilities.cs
rename to src/Microsoft.AspNet.Mvc.Xml/FormattingUtilities.cs
diff --git a/src/Microsoft.AspNet.Mvc.Xml/IUnwrappable.cs b/src/Microsoft.AspNet.Mvc.Xml/IUnwrappable.cs
new file mode 100644
index 0000000000..fb0cdc9ce8
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/IUnwrappable.cs
@@ -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
+{
+ ///
+ /// Defines an interface for objects to be un-wrappable after deserialization.
+ ///
+ public interface IUnwrappable
+ {
+ ///
+ /// Unwraps an object.
+ ///
+ /// The type to which the object should be un-wrapped to.
+ /// The un-wrapped object.
+ object Unwrap(Type declaredType);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/IWrapperProvider.cs b/src/Microsoft.AspNet.Mvc.Xml/IWrapperProvider.cs
new file mode 100644
index 0000000000..73ed4b63a3
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/IWrapperProvider.cs
@@ -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
+{
+ ///
+ /// Defines an interface for wrapping objects for serialization or deserialization into xml.
+ ///
+ public interface IWrapperProvider
+ {
+ ///
+ /// Gets the wrapping type.
+ ///
+ Type WrappingType { get; }
+
+ ///
+ /// Wraps the given object to the wrapping type provided by .
+ ///
+ /// The original non-wrapped object.
+ /// Returns a wrapped object.
+ object Wrap(object original);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/IWrapperProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Xml/IWrapperProviderFactory.cs
new file mode 100644
index 0000000000..6a3c6fdcc2
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/IWrapperProviderFactory.cs
@@ -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
+{
+ ///
+ /// Create a given a .
+ ///
+ public interface IWrapperProviderFactory
+ {
+ ///
+ /// Gets the for the provided context.
+ ///
+ /// The .
+ /// A wrapping provider if the factory decides to wrap the type, else null.
+ IWrapperProvider GetProvider(WrapperProviderContext context);
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Xml/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..fb22746534
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//
+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);
+
+ ///
+ /// The type must be an interface and must be or derive from '{0}'.
+ ///
+ internal static string EnumerableWrapperProvider_InvalidSourceEnumerableOfT
+ {
+ get { return GetString("EnumerableWrapperProvider_InvalidSourceEnumerableOfT"); }
+ }
+
+ ///
+ /// The type must be an interface and must be or derive from '{0}'.
+ ///
+ internal static string FormatEnumerableWrapperProvider_InvalidSourceEnumerableOfT(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("EnumerableWrapperProvider_InvalidSourceEnumerableOfT"), p0);
+ }
+
+ ///
+ /// The object to be wrapped must be of type '{0}' but was of type '{1}'.
+ ///
+ internal static string WrapperProvider_MismatchType
+ {
+ get { return GetString("WrapperProvider_MismatchType"); }
+ }
+
+ ///
+ /// The object to be wrapped must be of type '{0}' but was of type '{1}'.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Resources.resx b/src/Microsoft.AspNet.Mvc.Xml/Resources.resx
new file mode 100644
index 0000000000..d2c7be467a
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/Resources.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The type must be an interface and must be or derive from '{0}'.
+
+
+ The object to be wrapped must be of type '{0}' but was of type '{1}'.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapper.cs b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapper.cs
index f25addfc21..c374cb26fd 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapper.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapper.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
@@ -9,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Wrapper class for to enable it to be serialized by the xml formatters.
///
[XmlRoot("Error")]
- public sealed class SerializableErrorWrapper : IXmlSerializable
+ public sealed class SerializableErrorWrapper : IXmlSerializable, IUnwrappable
{
// Note: XmlSerializer requires to have default constructor
public SerializableErrorWrapper()
@@ -83,61 +86,10 @@ namespace Microsoft.AspNet.Mvc.Xml
}
}
- ///
- /// Gets the
- ///
- ///
- ///
- ///
- public static object UnwrapSerializableErrorObject([NotNull] Type modelType, object deserializedObject)
+ ///
+ public object Unwrap([NotNull] Type declaredType)
{
- // Since we expect users to typically bind with SerializableError type,
- // we should try to unwrap and get the actual SerializableError.
- if (modelType == typeof(SerializableError))
- {
- var serializableErrorWrapper = deserializedObject as SerializableErrorWrapper;
- if (serializableErrorWrapper != null)
- {
- deserializedObject = serializableErrorWrapper.SerializableError;
- }
- }
-
- return deserializedObject;
- }
-
- ///
- /// Checks if an object is an instance of type and if yes,
- /// gets and returns the wrapped object in it.
- ///
- /// An
- ///
- public static object WrapSerializableErrorObject(object obj)
- {
- var serializableError = obj as SerializableError;
- if (serializableError == null)
- {
- return obj;
- }
-
- return new SerializableErrorWrapper(serializableError);
- }
-
- ///
- /// Checks if the given type is of type and if yes, returns
- /// the wrapper type .
- ///
- /// The type to be checked
- /// type, else the original type.
- public static Type CreateSerializableType([NotNull] Type type)
- {
- // Since the type "SerializableError" is not compatible
- // with the xml serializers, we create a compatible wrapper type for serialization.
- if (type == typeof(SerializableError))
- {
- type = typeof(SerializableErrorWrapper);
- }
-
- return type;
+ return SerializableError;
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProvider.cs b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProvider.cs
new file mode 100644
index 0000000000..208d372f3e
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProvider.cs
@@ -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
+{
+ ///
+ /// Wraps the object of type .
+ ///
+ public class SerializableErrorWrapperProvider : IWrapperProvider
+ {
+ ///
+ public Type WrappingType
+ {
+ get
+ {
+ return typeof(SerializableErrorWrapper);
+ }
+ }
+
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProviderFactory.cs
new file mode 100644
index 0000000000..e779971223
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/SerializableErrorWrapperProviderFactory.cs
@@ -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
+{
+ ///
+ /// Creates an for the type .
+ ///
+ public class SerializableErrorWrapperProviderFactory : IWrapperProviderFactory
+ {
+ ///
+ /// Creates an instance of if the provided
+ /// declared type is .
+ ///
+ ///
+ /// An instance of if the provided
+ /// declared type is , else null.
+ public IWrapperProvider GetProvider([NotNull] WrapperProviderContext context)
+ {
+ if (context.DeclaredType == typeof(SerializableError))
+ {
+ return new SerializableErrorWrapperProvider();
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderContext.cs b/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderContext.cs
new file mode 100644
index 0000000000..b2b9a744ff
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderContext.cs
@@ -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
+{
+ ///
+ /// The context used by an to wrap or un-wrap types.
+ ///
+ public class WrapperProviderContext
+ {
+ ///
+ /// Initializes a .
+ ///
+ /// The declared type of the object that needs to be wrapped.
+ /// if the wrapper provider is invoked during
+ /// serialization, otherwise .
+ public WrapperProviderContext([NotNull] Type declaredType, bool isSerialization)
+ {
+ DeclaredType = declaredType;
+ IsSerialization = isSerialization;
+ }
+
+ ///
+ /// The declared type which could be wrapped/un-wrapped by a different type
+ /// during serialization or de-serializatoin.
+ ///
+ public Type DeclaredType { get; }
+
+ ///
+ /// if a wrapper provider is invoked during serialization,
+ /// otherwise.
+ ///
+ public bool IsSerialization { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderFactoriesExtensions.cs b/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderFactoriesExtensions.cs
new file mode 100644
index 0000000000..6a2efd5c50
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Xml/WrapperProviderFactoriesExtensions.cs
@@ -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
+{
+ ///
+ /// Extension methods for .
+ ///
+ public static class WrapperProviderFactoriesExtensions
+ {
+ ///
+ /// Gets an instance of for the supplied
+ /// type.
+ ///
+ /// A list of .
+ /// The .
+ /// An instance of if there is a wrapping provider for the
+ /// supplied type, else null.
+ public static IWrapperProvider GetWrapperProvider(
+ [NotNull] this IEnumerable wrapperProviderFactories,
+ [NotNull] WrapperProviderContext wrapperProviderContext)
+ {
+ foreach (var wrapperProviderFactory in wrapperProviderFactories)
+ {
+ var wrapperProvider = wrapperProviderFactory.GetProvider(wrapperProviderContext);
+ if (wrapperProvider != null)
+ {
+ return wrapperProvider;
+ }
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
similarity index 82%
rename from src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerInputFormatter.cs
rename to src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
index cec1a3c38d..f9ae5007fa 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
@@ -31,17 +31,28 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedEncodings = new List();
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
+
SupportedMediaTypes = new List();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
+
_serializerSettings = new DataContractSerializerSettings();
+
+ WrapperProviderFactories = new List();
+ WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
- ///
- public IList SupportedMediaTypes { get; private set; }
+ ///
+ /// Gets the list of to
+ /// provide the wrapping type for de-serialization.
+ ///
+ public IList WrapperProviderFactories { get; }
///
- public IList SupportedEncodings { get; private set; }
+ public IList SupportedMediaTypes { get; }
+
+ ///
+ public IList SupportedEncodings { get; }
///
/// Indicates the acceptable input XML depth.
@@ -127,7 +138,10 @@ namespace Microsoft.AspNet.Mvc.Xml
/// The type to which the XML will be deserialized.
protected virtual Type GetSerializableType([NotNull] Type declaredType)
{
- return SerializableErrorWrapper.CreateSerializableType(declaredType);
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(declaredType, isSerialization: false));
+
+ return wrapperProvider?.WrappingType ?? declaredType;
}
///
@@ -157,9 +171,21 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{
var type = GetSerializableType(context.ModelType);
- var dataContractSerializer = CreateSerializer(type);
- var deserializedObject = dataContractSerializer.ReadObject(xmlReader);
- deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
+
+ var serializer = CreateSerializer(type);
+
+ var deserializedObject = serializer.ReadObject(xmlReader);
+
+ // Unwrap only if the original type was wrapped.
+ if (type != context.ModelType)
+ {
+ var unwrappable = deserializedObject as IUnwrappable;
+ if (unwrappable != null)
+ {
+ deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType);
+ }
+ }
+
return Task.FromResult(deserializedObject);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerOutputFormatter.cs
similarity index 69%
rename from src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerOutputFormatter.cs
rename to src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerOutputFormatter.cs
index a751f202f9..1b5da1437a 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlDataContractSerializerOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerOutputFormatter.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
@@ -40,9 +41,20 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
+
_serializerSettings = new DataContractSerializerSettings();
+
+ WrapperProviderFactories = new List();
+ WrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(WrapperProviderFactories));
+ WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
+ ///
+ /// Gets the list of to
+ /// provide the wrapping type for serialization.
+ ///
+ public IList WrapperProviderFactories { get; }
+
///
/// Gets the settings to be used by the XmlWriter.
///
@@ -72,24 +84,38 @@ namespace Microsoft.AspNet.Mvc.Xml
/// The declared type.
/// The runtime type.
/// The type of the object to be serialized.
- protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
+ protected virtual Type ResolveType(Type declaredType, Type runtimeType)
{
- Type type = declaredType;
if (declaredType == null || declaredType == typeof(object))
{
if (runtimeType != null)
{
- type = runtimeType;
+ return runtimeType;
}
}
- return SerializableErrorWrapper.CreateSerializableType(type);
+ return declaredType;
+ }
+
+ ///
+ /// Gets the type to be serialized.
+ ///
+ /// The original type to be serialized
+ /// The original or wrapped type provided by any s.
+ protected virtual Type GetSerializableType(Type type)
+ {
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(type, isSerialization: true));
+
+ return wrapperProvider?.WrappingType ?? type;
}
///
protected override bool CanWriteType(Type declaredType, Type runtimeType)
{
- return CreateSerializer(GetSerializableType(declaredType, runtimeType)) != null;
+ var type = ResolveType(declaredType, runtimeType);
+
+ return CreateSerializer(GetSerializableType(type)) != null;
}
///
@@ -138,12 +164,26 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var outputStream = new DelegatingStream(innerStream))
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
{
- var runtimeType = context.Object == null ? null : context.Object.GetType();
+ var obj = context.Object;
+ var runtimeType = obj?.GetType();
- var type = GetSerializableType(context.DeclaredType, runtimeType);
- var dataContractSerializer = CreateSerializer(type);
- var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
- dataContractSerializer.WriteObject(xmlWriter, responseObject);
+ var resolvedType = ResolveType(context.DeclaredType, runtimeType);
+
+ var wrappingType = GetSerializableType(resolvedType);
+
+ // Wrap the object only if there is a wrapping type.
+ if (wrappingType != null && wrappingType != resolvedType)
+ {
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(
+ declaredType: resolvedType,
+ isSerialization: true));
+
+ obj = wrapperProvider.Wrap(obj);
+ }
+
+ var dataContractSerializer = CreateSerializer(wrappingType);
+ dataContractSerializer.WriteObject(xmlWriter, obj);
}
return Task.FromResult(true);
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
similarity index 78%
rename from src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerInputFormatter.cs
rename to src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
index 6b9611c71a..d553064849 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerInputFormatter.cs
@@ -30,16 +30,26 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedEncodings = new List();
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
+
SupportedMediaTypes = new List();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
+
+ WrapperProviderFactories = new List();
+ WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
- ///
- public IList SupportedMediaTypes { get; private set; }
+ ///
+ /// Gets the list of to
+ /// provide the wrapping type for de-serialization.
+ ///
+ public IList WrapperProviderFactories { get; }
///
- public IList SupportedEncodings { get; private set; }
+ public IList SupportedMediaTypes { get; }
+
+ ///
+ public IList SupportedEncodings { get; }
///
/// Indicates the acceptable input XML depth.
@@ -96,7 +106,10 @@ namespace Microsoft.AspNet.Mvc.Xml
/// The type to which the XML will be deserialized.
protected virtual Type GetSerializableType([NotNull] Type declaredType)
{
- return SerializableErrorWrapper.CreateSerializableType(declaredType);
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(declaredType, isSerialization: false));
+
+ return wrapperProvider?.WrappingType ?? declaredType;
}
///
@@ -114,7 +127,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// Called during deserialization to get the .
///
/// The used during deserialization.
- protected virtual XmlSerializer CreateXmlSerializer(Type type)
+ protected virtual XmlSerializer CreateSerializer(Type type)
{
return new XmlSerializer(type);
}
@@ -136,9 +149,21 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{
var type = GetSerializableType(context.ModelType);
- var xmlSerializer = CreateXmlSerializer(type);
- var deserializedObject = xmlSerializer.Deserialize(xmlReader);
- deserializedObject = SerializableErrorWrapper.UnwrapSerializableErrorObject(context.ModelType, deserializedObject);
+
+ var serializer = CreateSerializer(type);
+
+ var deserializedObject = serializer.Deserialize(xmlReader);
+
+ // Unwrap only if the original type was wrapped.
+ if (type != context.ModelType)
+ {
+ var unwrappable = deserializedObject as IUnwrappable;
+ if (unwrappable != null)
+ {
+ deserializedObject = unwrappable.Unwrap(declaredType: context.ModelType);
+ }
+ }
+
return Task.FromResult(deserializedObject);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerOutputFormatter.cs
similarity index 61%
rename from src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerOutputFormatter.cs
rename to src/Microsoft.AspNet.Mvc.Xml/XmlSerializerOutputFormatter.cs
index b96dff87e3..cde4a7ac53 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/Formatters/XmlSerializerOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlSerializerOutputFormatter.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml;
@@ -38,8 +39,18 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
+
+ WrapperProviderFactories = new List();
+ WrapperProviderFactories.Add(new EnumerableWrapperProviderFactory(WrapperProviderFactories));
+ WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
}
+ ///
+ /// Gets the list of to
+ /// provide the wrapping type for serialization.
+ ///
+ public IList WrapperProviderFactories { get; }
+
///
/// Gets the settings to be used by the XmlWriter.
///
@@ -48,27 +59,41 @@ namespace Microsoft.AspNet.Mvc.Xml
///
/// Gets the type of the object to be serialized.
///
- /// The declared type.
- /// The runtime type.
- /// The type of the object to be serialized.
- protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
+ /// The declared type of the object.
+ /// The runtime type of the object
+ /// A type that needs to be serialized.
+ protected virtual Type ResolveType(Type declaredType, Type runtimeType)
{
- var type = declaredType;
if (declaredType == null || declaredType == typeof(object))
{
if (runtimeType != null)
{
- type = runtimeType;
+ return runtimeType;
}
}
- return SerializableErrorWrapper.CreateSerializableType(type);
+ return declaredType;
+ }
+
+ ///
+ /// Gets the type to be serialized.
+ ///
+ /// The original type to be serialized
+ /// The original or wrapped type provided by any .
+ protected virtual Type GetSerializableType(Type type)
+ {
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(type, isSerialization: true));
+
+ return wrapperProvider?.WrappingType ?? type;
}
///
protected override bool CanWriteType(Type declaredType, Type runtimeType)
{
- return CreateSerializer(GetSerializableType(declaredType, runtimeType)) != null;
+ var type = ResolveType(declaredType, runtimeType);
+
+ return CreateSerializer(GetSerializableType(type)) != null;
}
///
@@ -115,12 +140,26 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var outputStream = new DelegatingStream(innerStream))
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
{
- var runtimeType = context.Object == null ? null : context.Object.GetType();
+ var obj = context.Object;
+ var runtimeType = obj?.GetType();
- var type = GetSerializableType(context.DeclaredType, runtimeType);
- var xmlSerializer = CreateSerializer(type);
- var responseObject = SerializableErrorWrapper.WrapSerializableErrorObject(context.Object);
- xmlSerializer.Serialize(xmlWriter, responseObject);
+ var resolvedType = ResolveType(context.DeclaredType, runtimeType);
+
+ var wrappingType = GetSerializableType(resolvedType);
+
+ // Wrap the object only if there is a wrapping type.
+ if (wrappingType != null && wrappingType != resolvedType)
+ {
+ var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(
+ new WrapperProviderContext(
+ declaredType: resolvedType,
+ isSerialization: true));
+
+ obj = wrapperProvider.Wrap(obj);
+ }
+
+ var xmlSerializer = CreateSerializer(wrappingType);
+ xmlSerializer.Serialize(xmlWriter, obj);
}
return Task.FromResult(true);
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj
index 1b773ab251..1225141df4 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj
@@ -14,4 +14,9 @@
2.0
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs
index 588f1d043c..2e926dc14b 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs
@@ -20,9 +20,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{
private readonly IServiceProvider _provider = TestHelper.CreateServices("ActionResultsWebSite");
private readonly Action _app = new Startup().Configure;
- private const string sampleIntError = "The field SampleInt must be between 10 and 100.";
- private const string sampleStringError =
- "The field SampleString must be a string or array type with a minimum length of '15'.";
[Fact]
public async Task BadRequestResult_CanBeReturned()
@@ -188,43 +185,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync());
}
- [Theory]
- [InlineData("http://localhost/Home/Index",
- "application/json;charset=utf-8",
- "{\"test.SampleInt\":[\"" + sampleIntError + "\"]," +
- "\"test.SampleString\":" +
- "[\"" + sampleStringError + "\"]}")]
- [InlineData("http://localhost/Home/Index",
- "application/xml;charset=utf-8",
- "" + sampleIntError + "" +
- "" + sampleStringError +
- "")]
- [InlineData("http://localhost/XmlSerializer/GetSerializableError",
- "application/xml;charset=utf-8",
- "" + sampleIntError + "" +
- "" + sampleStringError +
- "")]
- public async Task SerializableErrorIsReturnedInExpectedFormat(string url, string outputFormat, string output)
- {
- // Arrange
- var server = TestServer.Create(_provider, _app);
- var client = server.CreateClient();
-
- var input = "" +
- "" +
- "2foo";
- var request = new HttpRequestMessage(HttpMethod.Post, url);
- request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(outputFormat));
- request.Content = new StringContent(input, Encoding.UTF8, "application/xml");
-
- // Act
- var response = await client.SendAsync(request);
-
- // Assert
- Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
- Assert.Equal(output, await response.Content.ReadAsStringAsync());
- }
-
[Fact]
public async Task SerializableError_CanSerializeNormalObjects()
{
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonOutputFormatterTests.cs
index 0dff6199ee..b1ef1e218b 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonOutputFormatterTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonOutputFormatterTests.cs
@@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
@@ -42,5 +46,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var actualBody = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, actualBody);
}
+
+ [Fact]
+ public async Task SerializableErrorIsReturnedInExpectedFormat()
+ {
+ // Arrange
+ var server = TestServer.Create(_provider, _app);
+ var client = server.CreateClient();
+
+ var input = "" +
+ "" +
+ "2foo";
+
+ 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());
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj
index f97db678df..be9e29520d 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj
@@ -10,6 +10,12 @@
..\..\artifacts\obj\$(MSBuildProjectName)..\..\artifacts\bin\$(MSBuildProjectName)\
+
+ Microsoft.AspNet.Mvc.FunctionalTests
+
+
+ Microsoft.AspNet.Mvc.FunctionalTests
+ 2.0
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/SerializableErrorTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/SerializableErrorTests.cs
index 0111208e7f..a4cd4d740f 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/SerializableErrorTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/SerializableErrorTests.cs
@@ -64,5 +64,32 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var responseData = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedXml, responseData);
}
+
+ [Theory]
+ [InlineData("application/xml-xmlser")]
+ [InlineData("application/xml-dcs")]
+ public async Task IsReturnedInExpectedFormat(string acceptHeader)
+ {
+ // Arrange
+ var server = TestServer.Create(_services, _app);
+ var client = server.CreateClient();
+ var input = "" +
+ "" +
+ "2foo";
+ var expected = "The field Id must be between 10 and 100." +
+ "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(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);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerFormattersWrappingTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerFormattersWrappingTest.cs
new file mode 100644
index 0000000000..a36aa61aee
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerFormattersWrappingTest.cs
@@ -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 _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("" +
+ "1020",
+ 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("" +
+ "value1value2",
+ 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("",
+ 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("",
+ 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("" +
+ "3510Mike35" +
+ "11Jimmy",
+ 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("",
+ 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("",
+ 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("" +
+ "key1-errorkey2-error" +
+ "key1-errorkey2-error" +
+ "",
+ result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlSerializerFormattersWrappingTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlSerializerFormattersWrappingTest.cs
new file mode 100644
index 0000000000..b1d7968343
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlSerializerFormattersWrappingTest.cs
@@ -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 _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("10" +
+ "20",
+ 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("value1" +
+ "value2",
+ 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("",
+ 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("",
+ 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("10" +
+ "Mike3511" +
+ "Jimmy35",
+ 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("",
+ 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("",
+ 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("key1-error" +
+ "key2-errorkey1-error" +
+ "key2-error",
+ result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumerableTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumerableTest.cs
new file mode 100644
index 0000000000..0f9ec100e8
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumerableTest.cs
@@ -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(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(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(
+ 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(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(
+ errors, new SerializableErrorWrapperProvider());
+
+ // Act and Assert
+ Assert.Empty(delegatingEnumerable);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumeratorTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumeratorTest.cs
new file mode 100644
index 0000000000..a432dbc9b9
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/DelegatingEnumeratorTest.cs
@@ -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>();
+ innerEnumerator.Setup(innerEnum => innerEnum.Dispose())
+ .Verifiable();
+ var delegatingEnumerator = new DelegatingEnumerator(
+ innerEnumerator.Object,
+ wrapperProvider: null);
+
+ // Act
+ delegatingEnumerator.Dispose();
+
+ // Assert
+ innerEnumerator.Verify();
+ }
+
+ [Fact]
+ public void MoveNextCalled_OnInnerEnumerator()
+ {
+ // Arrange
+ var innerEnumerator = new Mock>();
+ innerEnumerator.Setup(innerEnum => innerEnum.MoveNext())
+ .Verifiable();
+ var delegatingEnumerator = new DelegatingEnumerator(
+ innerEnumerator.Object,
+ wrapperProvider: null);
+
+ // Act
+ var available = delegatingEnumerator.MoveNext();
+
+ // Assert
+ innerEnumerator.Verify();
+ }
+
+ [Fact]
+ public void ResetCalled_OnInnerEnumerator()
+ {
+ // Arrange
+ var innerEnumerator = new Mock>();
+ innerEnumerator.Setup(innerEnum => innerEnum.Reset())
+ .Verifiable();
+ var delegatingEnumerator = new DelegatingEnumerator(
+ innerEnumerator.Object,
+ wrapperProvider: null);
+
+ // Act
+ delegatingEnumerator.Reset();
+
+ // Assert
+ innerEnumerator.Verify();
+ }
+
+ [Fact]
+ public void CurrentCalled_OnInnerEnumerator()
+ {
+ // Arrange
+ var innerEnumerator = new Mock>();
+ innerEnumerator.SetupGet(innerEnum => innerEnum.Current)
+ .Verifiable();
+ var delegatingEnumerator = new DelegatingEnumerator(
+ innerEnumerator.Object,
+ wrapperProvider: null);
+
+ // Act
+ var obj = delegatingEnumerator.Current;
+
+ // Assert
+ innerEnumerator.Verify();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderFactoryTest.cs
new file mode 100644
index 0000000000..7d6770fa31
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderFactoryTest.cs
@@ -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 EnumerableOfTInterfaceData
+ {
+ get
+ {
+ var serializableError = new SerializableError();
+ serializableError.Add("key1", "key1-error");
+
+ return new TheoryData
+ {
+ {
+ typeof(IEnumerable),
+ new [] { "value1", "value2" },
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ new [] { 10, 20 },
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ new [] { new Person() { Id =10, Name = "John" } },
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ new [] { serializableError },
+ typeof(DelegatingEnumerable)
+ },
+ };
+ }
+ }
+
+ [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 QueryableOfTInterfaceData
+ {
+ get
+ {
+ var serializableError = new SerializableError();
+ serializableError.Add("key1", "key1-error");
+
+ return new TheoryData
+ {
+ {
+ typeof(IEnumerable),
+ (new [] { "value1", "value2" }).AsQueryable(),
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ (new [] { 10, 20 }).AsQueryable(),
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ (new [] { new Person() { Id =10, Name = "John" } }).AsQueryable(),
+ typeof(DelegatingEnumerable)
+ },
+ {
+ typeof(IEnumerable),
+ (new [] { serializableError }).AsQueryable(),
+ typeof(DelegatingEnumerable)
+ },
+ };
+ }
+ }
+
+ [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 ConcreteEnumerableOfTData
+ {
+ get
+ {
+ var serializableError = new SerializableError();
+ serializableError.Add("key1", "key1-error");
+
+ return new TheoryData
+ {
+ {
+ typeof(string), // 'string' implements IEnumerable
+ "value"
+ },
+ {
+ typeof(List),
+ (new [] { 10, 20 }).ToList()
+ },
+ {
+ typeof(List),
+ (new [] { new Person() { Id =10, Name = "John" } }).ToList()
+ },
+ {
+ typeof(List),
+ (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 GetWrapperProviderFactories()
+ {
+ var wrapperProviderFactories = new List();
+ 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
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderTest.cs
new file mode 100644
index 0000000000..c6b23f071a
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/EnumerableWrapperProviderTest.cs
@@ -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),
+ typeof(DelegatingEnumerable))]
+ [InlineData(typeof(IQueryable),
+ typeof(DelegatingEnumerable))]
+ [InlineData(typeof(ICollection),
+ typeof(DelegatingEnumerable))]
+ [InlineData(typeof(IList),
+ typeof(DelegatingEnumerable))]
+ 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);
+ var wrapperProvider = new EnumerableWrapperProvider(
+ declaredEnumerableOfT,
+ elementWrapperProvider: null);
+
+ // Act
+ var wrapped = wrapperProvider.Wrap(new int[] { });
+
+ // Assert
+ Assert.Equal(typeof(DelegatingEnumerable), wrapperProvider.WrappingType);
+ Assert.NotNull(wrapped);
+ var delegatingEnumerable = wrapped as DelegatingEnumerable;
+ Assert.NotNull(delegatingEnumerable);
+ Assert.Equal(0, delegatingEnumerable.Count());
+ }
+
+ [Fact]
+ public void Ignores_NullInstances()
+ {
+ // Arrange
+ var declaredEnumerableOfT = typeof(IEnumerable);
+ var wrapperProvider = new EnumerableWrapperProvider(
+ declaredEnumerableOfT,
+ elementWrapperProvider: null);
+
+ // Act
+ var wrapped = wrapperProvider.Wrap(null);
+
+ // Assert
+ Assert.Equal(typeof(DelegatingEnumerable), wrapperProvider.WrappingType);
+ Assert.Null(wrapped);
+ }
+
+ [Theory]
+ [InlineData(typeof(string))]
+ [InlineData(typeof(List))]
+ [InlineData(typeof(List))]
+ [InlineData(typeof(List))]
+ [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(() => new EnumerableWrapperProvider(
+ declaredType,
+ elementWrapperProvider: null));
+
+ Assert.Equal(expectedMessage, ex.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/Models/Person.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/Models/Person.cs
new file mode 100644
index 0000000000..5314dfd257
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/Models/Person.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/Models/PersonList.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/Models/PersonList.cs
new file mode 100644
index 0000000000..52f97a0b04
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/Models/PersonList.cs
@@ -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
+ {
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapper.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapper.cs
new file mode 100644
index 0000000000..06690c1895
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapper.cs
@@ -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 };
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProvider.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProvider.cs
new file mode 100644
index 0000000000..dfdd36ad39
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProvider.cs
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProviderFactory.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProviderFactory.cs
new file mode 100644
index 0000000000..d341a2794f
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/PersonWrapperProviderFactory.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableErrorWrapperProviderTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableErrorWrapperProviderTest.cs
new file mode 100644
index 0000000000..d7acd20e60
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableErrorWrapperProviderTest.cs
@@ -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(() => wrapperProvider.Wrap(person));
+ Assert.Equal(expectedMessage, exception.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableWrapperProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableWrapperProviderFactoryTest.cs
new file mode 100644
index 0000000000..785fb61247
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/SerializableWrapperProviderFactoryTest.cs
@@ -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);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
index 52b2d20ef7..0785d58fe4 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
@@ -15,7 +15,7 @@ using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
-namespace Microsoft.AspNet.Mvc
+namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlDataContractSerializerInputFormatterTest
{
@@ -345,26 +345,6 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expectedString, levelOneModel.sampleString);
}
- [Fact]
- public async Task ReadAsync_ReadsSerializableErrorXml()
- {
- // Arrange
- var serializableErrorXml = "" +
- "Test Error 1 Test Error 2Test Error 3";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(serializableErrorXml);
- var context = GetInputFormatterContext(contentBytes, typeof(SerializableError));
-
- // Act
- var model = await formatter.ReadAsync(context);
-
- // Assert
- var serializableError = model as SerializableError;
- Assert.NotNull(serializableError);
- Assert.Equal("Test Error 1 Test Error 2", serializableError["key1"]);
- Assert.Equal("Test Error 3", serializableError["key2"]);
- }
-
[Fact]
public async Task ReadAsync_ThrowsWhenNotConfiguredWithRootName()
{
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs
index 9a526b6ea6..31d97a6946 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerOutputFormatterTest.cs
@@ -15,7 +15,7 @@ using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
-namespace Microsoft.AspNet.Mvc
+namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlDataContractSerializerOutputFormatterTest
{
@@ -134,7 +134,7 @@ namespace Microsoft.AspNet.Mvc
var sampleInput = new DummyClass { SampleInt = 10 };
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
var formatter = new XmlDataContractSerializerOutputFormatter(writerSettings);
- var expectedOutput = ""+
+ var expectedOutput = "" +
"" +
"10";
@@ -222,7 +222,7 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("" +
"" +
"10",
- new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body,
+ new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body,
Encoding.UTF8).ReadToEnd());
}
@@ -348,7 +348,7 @@ namespace Microsoft.AspNet.Mvc
[Theory]
[MemberData(nameof(TypesForGetSupportedContentTypes))]
- public void GetSupportedContentTypes_ReturnsSupportedTypes(Type declaredType,
+ public void GetSupportedContentTypes_ReturnsSupportedTypes(Type declaredType,
Type runtimeType, object expectedOutput)
{
// Arrange
@@ -378,7 +378,7 @@ namespace Microsoft.AspNet.Mvc
var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass));
// Act & Assert
- await Assert.ThrowsAsync(typeof(SerializationException),
+ await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}
@@ -394,7 +394,7 @@ namespace Microsoft.AspNet.Mvc
var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType());
// Act & Assert
- await Assert.ThrowsAsync(typeof(SerializationException),
+ await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
index cfadaba9a0..4f1dab7dc2 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerInputFormatterTest.cs
@@ -15,7 +15,7 @@ using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
-namespace Microsoft.AspNet.Mvc
+namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlSerializerInputFormatterTest
{
@@ -346,26 +346,6 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), levelOneModel.SampleDate);
}
- [Fact]
- public async Task ReadAsync_ReadsSerializableErrorXml()
- {
- // Arrange
- var serializableErrorXml = "" +
- "Test Error 1 Test Error 2Test Error 3";
- var formatter = new XmlSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(serializableErrorXml);
- var context = GetInputFormatterContext(contentBytes, typeof(SerializableError));
-
- // Act
- var model = await formatter.ReadAsync(context);
-
- // Assert
- var serializableError = model as SerializableError;
- Assert.NotNull(serializableError);
- Assert.Equal("Test Error 1 Test Error 2", serializableError["key1"]);
- Assert.Equal("Test Error 3", serializableError["key2"]);
- }
-
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var actionContext = GetActionContext(contentBytes);
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs
index cf54c35344..0a94a3b23c 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlSerializerOutputFormatterTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
@@ -13,7 +14,7 @@ using Microsoft.AspNet.Mvc.Xml;
using Moq;
using Xunit;
-namespace Microsoft.AspNet.Mvc
+namespace Microsoft.AspNet.Mvc.Xml
{
public class XmlSerializerOutputFormatterTest
{
@@ -243,7 +244,7 @@ namespace Microsoft.AspNet.Mvc
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
}
-
+
public static IEnumerable