diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs
index d8f7cb52c5..5d96c4d1b2 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs
@@ -2,11 +2,8 @@
// 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.Text;
+using System.Runtime.Serialization;
using System.Xml;
-using Microsoft.AspNet.Mvc.Core;
-using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc
{
@@ -17,6 +14,10 @@ namespace Microsoft.AspNet.Mvc
{
public static readonly int DefaultMaxDepth = 32;
+#if NET45
+ public static readonly XsdDataContractExporter XsdDataContractExporter = new XsdDataContractExporter();
+#endif
+
///
/// Gets the default Reader Quotas for XmlReader.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs
index 383117c109..c3b0708dc7 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs
@@ -67,7 +67,25 @@ namespace Microsoft.AspNet.Mvc
encoding = encoding ?? SupportedEncodings.FirstOrDefault();
return encoding;
}
-
+
+ ///
+ /// Gets the type of the object to be serialized.
+ ///
+ /// The context which contains the object to be serialized.
+ /// The type of the object to be serialized.
+ public virtual Type GetObjectType([NotNull] OutputFormatterContext context)
+ {
+ if (context.DeclaredType == null)
+ {
+ if (context.Object != null)
+ {
+ return context.Object.GetType();
+ }
+ }
+
+ return context.DeclaredType;
+ }
+
///
public virtual bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerOutputFormatter.cs
index a2b53da271..02af1d6b6a 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerOutputFormatter.cs
@@ -37,9 +37,25 @@ namespace Microsoft.AspNet.Mvc
///
/// The type of object for which the serializer should be created.
/// A new instance of
- public virtual DataContractSerializer CreateDataContractSerializer([NotNull] Type type)
+ public override object CreateSerializer([NotNull] Type type)
{
- return new DataContractSerializer(type);
+ DataContractSerializer serializer = null;
+ try
+ {
+#if NET45
+ // Verify that type is a valid data contract by forcing the serializer to try to create a data contract
+ FormattingUtilities.XsdDataContractExporter.GetRootElementName(type);
+#endif
+ // If the serializer does not support this type it will throw an exception.
+ serializer = new DataContractSerializer(type);
+ }
+ catch (Exception)
+ {
+ // We do not surface the caught exception because if CanWriteResult returns
+ // false, then this Formatter is not picked up at all.
+ }
+
+ return serializer;
}
///
@@ -51,7 +67,7 @@ namespace Microsoft.AspNet.Mvc
tempWriterSettings.Encoding = context.SelectedEncoding;
using (var xmlWriter = CreateXmlWriter(response.Body, tempWriterSettings))
{
- var dataContractSerializer = CreateDataContractSerializer(context.DeclaredType);
+ var dataContractSerializer = (DataContractSerializer)CreateSerializer(GetObjectType(context));
dataContractSerializer.WriteObject(xmlWriter, context.Object);
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlOutputFormatter.cs
index cf9d1ccb35..f9135b3208 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlOutputFormatter.cs
@@ -1,6 +1,7 @@
// 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.IO;
using System.Xml;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
@@ -28,6 +29,27 @@ namespace Microsoft.AspNet.Mvc
///
public XmlWriterSettings WriterSettings { get; private set; }
+ ///
+ /// Returns a serializer to serialzie the particualr type.
+ ///
+ /// The type which needs to be serialized.
+ /// The serializer object.
+ public abstract object CreateSerializer(Type type);
+
+ ///
+ public override bool CanWriteResult([NotNull] OutputFormatterContext context, MediaTypeHeaderValue contentType)
+ {
+ if (base.CanWriteResult(context, contentType))
+ {
+ if (CreateSerializer(GetObjectType(context)) != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
///
/// Gets the default XmlWriterSettings.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerOutputFormatter.cs
index ea1223f658..81a74ea247 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerOutputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerOutputFormatter.cs
@@ -37,9 +37,21 @@ namespace Microsoft.AspNet.Mvc
///
/// The type of object for which the serializer should be created.
/// A new instance of
- public virtual XmlSerializer CreateXmlSerializer([NotNull] Type type)
+ public override object CreateSerializer([NotNull] Type type)
{
- return new XmlSerializer(type);
+ XmlSerializer serializer = null;
+ try
+ {
+ // If the serializer does not support this type it will throw an exception.
+ serializer = new XmlSerializer(type);
+ }
+ catch (Exception)
+ {
+ // We do not surface the caught exception because if CanWriteResult returns
+ // false, then this Formatter is not picked up at all.
+ }
+
+ return serializer;
}
///
@@ -51,7 +63,7 @@ namespace Microsoft.AspNet.Mvc
tempWriterSettings.Encoding = context.SelectedEncoding;
using (var xmlWriter = CreateXmlWriter(response.Body, tempWriterSettings))
{
- var xmlSerializer = CreateXmlSerializer(context.DeclaredType);
+ var xmlSerializer = (XmlSerializer)CreateSerializer(GetObjectType(context));
xmlSerializer.Serialize(xmlWriter, context.Object);
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs
index 2370d00b66..21ed42dd72 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerOutputFormatterTests.cs
@@ -2,11 +2,13 @@
// 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.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
using Moq;
using Xunit;
@@ -180,6 +182,18 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
}
+ [Fact]
+ public void XmlDataContractSerializer_CanWriteResult_ReturnsTrue_ForWritableType()
+ {
+ // Arrange
+ var formatter = new XmlDataContractSerializerOutputFormatter(
+ XmlOutputFormatter.GetDefaultXmlWriterSettings());
+ var outputFormatterContext = GetOutputFormatterContext(null, typeof(Dictionary));
+
+ // Act & Assert
+ Assert.True(formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")));
+ }
+
private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType,
string contentType = "application/xml; charset=utf-8")
{
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs
index 2ffd2ab87f..75b2a698f3 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerOutputFormatterTests.cs
@@ -2,10 +2,12 @@
// 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.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
using Moq;
using Xunit;
@@ -173,6 +175,30 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
}
+ [Fact]
+ public void XmlSerializer_CanWriteResult_ReturnsFalse_ForNonWritableType()
+ {
+ // Arrange
+ var formatter = new XmlSerializerOutputFormatter();
+ var outputFormatterContext = GetOutputFormatterContext(outputValue: null,
+ outputType: typeof(Dictionary));
+
+ // Act & Assert
+ Assert.False(formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")));
+ }
+
+ [Fact]
+ public void XmlDataContractSerializer_CanWriteResult_ReturnsTrue_ForWritableType()
+ {
+ // Arrange
+ var formatter = new XmlSerializerOutputFormatter();
+ var outputFormatterContext = GetOutputFormatterContext(outputValue: null,
+ outputType: typeof(string));
+
+ // Act & Assert
+ Assert.True(formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")));
+ }
+
private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType,
string contentType = "application/xml; charset=utf-8")
{
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlOutputFormatterTests.cs
index 35d74ef7e3..62611648ff 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlOutputFormatterTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlOutputFormatterTests.cs
@@ -62,5 +62,26 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">10",
new StreamReader(response.Body, Encoding.UTF8).ReadToEnd());
}
+
+ [Fact]
+ public async Task XmlSerializerFailsAndDataContractSerializerIsCalled()
+ {
+ // Arrange
+ var server = TestServer.Create(_services, _app);
+ var client = server.Handler;
+ var headers = new Dictionary();
+ headers.Add("Accept", new string[] { "application/xml;charset=utf-8" });
+
+ // Act
+ var response = await client.SendAsync("POST",
+ "http://localhost/DataContractSerializer/GetPerson?name=HelloWorld", headers, null, null);
+
+ //Assert
+ Assert.Equal(200, response.StatusCode);
+ Assert.Equal("" +
+ "HelloWorld",
+ new StreamReader(response.Body, Encoding.UTF8).ReadToEnd());
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
index 0c2ee64a2e..a56aec347d 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
@@ -4,12 +4,12 @@
},
"dependencies": {
"ActivatorWebSite": "",
+ "AntiForgeryWebSite": "",
"BasicWebSite": "",
"CompositeViewEngine": "",
"ConnegWebsite": "",
- "FormatterWebSite": "",
+ "FormatterWebSite": "",
"InlineConstraintsWebSite": "",
- "AntiForgeryWebSite": "",
"Microsoft.AspNet.TestHost": "1.0.0-*",
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "",
diff --git a/test/WebSites/FormatterWebSite/Controllers/DataContractSerializerController.cs b/test/WebSites/FormatterWebSite/Controllers/DataContractSerializerController.cs
new file mode 100644
index 0000000000..11b1c9ecc4
--- /dev/null
+++ b/test/WebSites/FormatterWebSite/Controllers/DataContractSerializerController.cs
@@ -0,0 +1,33 @@
+// 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 Microsoft.AspNet.Mvc;
+
+namespace FormatterWebSite
+{
+ ///
+ /// Summary description for DataContractSerializerController
+ ///
+ public class DataContractSerializerController : Controller
+ {
+ public override void OnActionExecuted(ActionExecutedContext context)
+ {
+ var result = context.Result as ObjectResult;
+ if (result != null)
+ {
+ result.Formatters.Add(new XmlSerializerOutputFormatter());
+ result.Formatters.Add(new XmlDataContractSerializerOutputFormatter());
+ }
+
+ base.OnActionExecuted(context);
+ }
+
+ [HttpPost]
+ public Person GetPerson(string name)
+ {
+ // The XmlSerializer should skip and the
+ // DataContractSerializer should pick up this output.
+ return new Person(name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/FormatterWebSite/Models/Person.cs b/test/WebSites/FormatterWebSite/Models/Person.cs
new file mode 100644
index 0000000000..a991a3c88d
--- /dev/null
+++ b/test/WebSites/FormatterWebSite/Models/Person.cs
@@ -0,0 +1,19 @@
+// 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.Runtime.Serialization;
+
+namespace FormatterWebSite
+{
+ [DataContract]
+ public class Person
+ {
+ public Person(string name)
+ {
+ Name = name;
+ }
+
+ [DataMember]
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file