[Fixes #1841] Change XML DCS and XmlSerializer output formatters to not derive from the base XmlOutputFormatter

This commit is contained in:
Kiran Challa 2015-01-19 10:14:10 -08:00 committed by Kiran Challa
parent d51dad9560
commit 0e9091f0eb
8 changed files with 202 additions and 110 deletions

View File

@ -34,5 +34,19 @@ namespace Microsoft.AspNet.Mvc
MaxStringContentLength = int.MaxValue MaxStringContentLength = int.MaxValue
}; };
} }
/// <summary>
/// Gets the default XmlWriterSettings.
/// </summary>
/// <returns>Default <see cref="XmlWriterSettings"/></returns>
public static XmlWriterSettings GetDefaultXmlWriterSettings()
{
return new XmlWriterSettings
{
OmitXmlDeclaration = true,
CloseOutput = false,
CheckCharacters = false
};
}
} }
} }

View File

@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -12,14 +14,14 @@ namespace Microsoft.AspNet.Mvc
/// This class handles serialization of objects /// This class handles serialization of objects
/// to XML using <see cref="DataContractSerializer"/> /// to XML using <see cref="DataContractSerializer"/>
/// </summary> /// </summary>
public class XmlDataContractSerializerOutputFormatter : XmlOutputFormatter public class XmlDataContractSerializerOutputFormatter : OutputFormatter
{ {
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="XmlDataContractSerializerOutputFormatter"/> /// Initializes a new instance of <see cref="XmlDataContractSerializerOutputFormatter"/>
/// with default XmlWriterSettings /// with default XmlWriterSettings
/// </summary> /// </summary>
public XmlDataContractSerializerOutputFormatter() public XmlDataContractSerializerOutputFormatter() :
: this(GetDefaultXmlWriterSettings()) this(FormattingUtilities.GetDefaultXmlWriterSettings())
{ {
} }
@ -28,8 +30,39 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
/// <param name="writerSettings">The settings to be used by the <see cref="DataContractSerializer"/>.</param> /// <param name="writerSettings">The settings to be used by the <see cref="DataContractSerializer"/>.</param>
public XmlDataContractSerializerOutputFormatter([NotNull] XmlWriterSettings writerSettings) public XmlDataContractSerializerOutputFormatter([NotNull] XmlWriterSettings writerSettings)
: base(writerSettings)
{ {
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
}
/// <summary>
/// Gets the settings to be used by the XmlWriter.
/// </summary>
public XmlWriterSettings WriterSettings { get; }
/// <summary>
/// Gets the type of the object to be serialized.
/// </summary>
/// <param name="declaredType">The declared type.</param>
/// <param name="runtimeType">The runtime type.</param>
/// <returns>The type of the object to be serialized.</returns>
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
{
if (declaredType == null ||
declaredType == typeof(object))
{
if (runtimeType != null)
{
return runtimeType;
}
}
return declaredType;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -62,6 +95,17 @@ namespace Microsoft.AspNet.Mvc
} }
} }
/// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given stream and the <see cref="WriterSettings"/>.
/// </summary>
/// <param name="writeStream">The stream on which the XmlWriter should operate on.</param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter([NotNull] Stream writeStream,
[NotNull] XmlWriterSettings xmlWriterSettings)
{
return XmlWriter.Create(writeStream, xmlWriterSettings);
}
/// <inheritdoc /> /// <inheritdoc />
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context) public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
{ {

View File

@ -1,77 +0,0 @@
// 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.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Abstract base class from which all XML Output Formatters derive from.
/// </summary>
public abstract class XmlOutputFormatter : OutputFormatter
{
public XmlOutputFormatter([NotNull] XmlWriterSettings xmlWriterSettings)
{
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = xmlWriterSettings;
}
/// <summary>
/// Gets or sets the settings to be used by the XmlWriter.
/// </summary>
public XmlWriterSettings WriterSettings { get; private set; }
/// <summary>
/// Gets the type of the object to be serialized.
/// </summary>
/// <param name="declaredType">The declared type.</param>
/// <param name="runtimeType">The runtime type.</param>
/// <returns>The type of the object to be serialized.</returns>
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
{
if (declaredType == null ||
declaredType == typeof(object))
{
if (runtimeType != null)
{
return runtimeType;
}
}
return declaredType;
}
/// <summary>
/// Gets the default XmlWriterSettings.
/// </summary>
/// <returns>Default <see cref="XmlWriterSettings"/></returns>
public static XmlWriterSettings GetDefaultXmlWriterSettings()
{
return new XmlWriterSettings
{
OmitXmlDeclaration = true,
CloseOutput = false,
CheckCharacters = false
};
}
/// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given stream and the WriterSettings.
/// </summary>
/// <param name="writeStream">The stream on which the XmlWriter should operate on.</param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter([NotNull] Stream writeStream,
[NotNull] XmlWriterSettings xmlWriterSettings)
{
return XmlWriter.Create(writeStream, xmlWriterSettings);
}
}
}

View File

@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -12,14 +14,14 @@ namespace Microsoft.AspNet.Mvc
/// This class handles serialization of objects /// This class handles serialization of objects
/// to XML using <see cref="XmlSerializer"/> /// to XML using <see cref="XmlSerializer"/>
/// </summary> /// </summary>
public class XmlSerializerOutputFormatter : XmlOutputFormatter public class XmlSerializerOutputFormatter : OutputFormatter
{ {
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="XmlSerializerOutputFormatter"/> /// Initializes a new instance of <see cref="XmlSerializerOutputFormatter"/>
/// with default XmlWriterSettings. /// with default XmlWriterSettings.
/// </summary> /// </summary>
public XmlSerializerOutputFormatter() public XmlSerializerOutputFormatter() :
: this(GetDefaultXmlWriterSettings()) this(FormattingUtilities.GetDefaultXmlWriterSettings())
{ {
} }
@ -28,8 +30,39 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
/// <param name="writerSettings">The settings to be used by the <see cref="XmlSerializer"/>.</param> /// <param name="writerSettings">The settings to be used by the <see cref="XmlSerializer"/>.</param>
public XmlSerializerOutputFormatter([NotNull] XmlWriterSettings writerSettings) public XmlSerializerOutputFormatter([NotNull] XmlWriterSettings writerSettings)
: base(writerSettings)
{ {
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
}
/// <summary>
/// Gets the settings to be used by the XmlWriter.
/// </summary>
public XmlWriterSettings WriterSettings { get; }
/// <summary>
/// Gets the type of the object to be serialized.
/// </summary>
/// <param name="declaredType">The declared type.</param>
/// <param name="runtimeType">The runtime type.</param>
/// <returns>The type of the object to be serialized.</returns>
protected virtual Type GetSerializableType(Type declaredType, Type runtimeType)
{
if (declaredType == null ||
declaredType == typeof(object))
{
if (runtimeType != null)
{
return runtimeType;
}
}
return declaredType;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -58,6 +91,17 @@ namespace Microsoft.AspNet.Mvc
} }
} }
/// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given stream and the <see cref="WriterSettings"/>.
/// </summary>
/// <param name="writeStream">The stream on which the XmlWriter should operate on.</param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter([NotNull] Stream writeStream,
[NotNull] XmlWriterSettings xmlWriterSettings)
{
return XmlWriter.Create(writeStream, xmlWriterSettings);
}
/// <inheritdoc /> /// <inheritdoc />
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context) public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
{ {

View File

@ -13,8 +13,7 @@ namespace Microsoft.AspNet.Mvc
/// <param name="options">The MvcOptions</param> /// <param name="options">The MvcOptions</param>
public static void AddXmlDataContractSerializerFormatter([NotNull] this MvcOptions options) public static void AddXmlDataContractSerializerFormatter([NotNull] this MvcOptions options)
{ {
options.OutputFormatters.Add( options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
new XmlDataContractSerializerOutputFormatter(XmlOutputFormatter.GetDefaultXmlWriterSettings()));
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter()); options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
} }

View File

@ -492,7 +492,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
new HttpNoContentOutputFormatter(), new HttpNoContentOutputFormatter(),
new StringOutputFormatter(), new StringOutputFormatter(),
new JsonOutputFormatter(), new JsonOutputFormatter(),
new XmlDataContractSerializerOutputFormatter(XmlSerializerOutputFormatter.GetDefaultXmlWriterSettings()) new XmlDataContractSerializerOutputFormatter()
}; };
var response = GetMockHttpResponse(); var response = GetMockHttpResponse();
@ -533,7 +533,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
new HttpNoContentOutputFormatter(), new HttpNoContentOutputFormatter(),
new StringOutputFormatter(), new StringOutputFormatter(),
new JsonOutputFormatter(), new JsonOutputFormatter(),
new XmlDataContractSerializerOutputFormatter(XmlSerializerOutputFormatter.GetDefaultXmlWriterSettings()) new XmlDataContractSerializerOutputFormatter()
}; };
var response = GetMockHttpResponse(); var response = GetMockHttpResponse();
@ -568,7 +568,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
new HttpNoContentOutputFormatter(), new HttpNoContentOutputFormatter(),
new StringOutputFormatter(), new StringOutputFormatter(),
new JsonOutputFormatter(), new JsonOutputFormatter(),
new XmlDataContractSerializerOutputFormatter(XmlSerializerOutputFormatter.GetDefaultXmlWriterSettings()) new XmlDataContractSerializerOutputFormatter()
}; };
var response = GetMockHttpResponse(); var response = GetMockHttpResponse();

View File

@ -84,13 +84,51 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead); Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
} }
[Fact]
public void DefaultConstructor_ExpectedWriterSettings_Created()
{
// Arrange and Act
var formatter = new XmlDataContractSerializerOutputFormatter();
// Assert
var writerSettings = formatter.WriterSettings;
Assert.NotNull(writerSettings);
Assert.True(writerSettings.OmitXmlDeclaration);
Assert.False(writerSettings.CloseOutput);
Assert.False(writerSettings.CheckCharacters);
}
[Fact]
public async Task SuppliedWriterSettings_TakeAffect()
{
// Arrange
var writerSettings = FormattingUtilities.GetDefaultXmlWriterSettings();
writerSettings.OmitXmlDeclaration = false;
var sampleInput = new DummyClass { SampleInt = 10 };
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
var formatter = new XmlDataContractSerializerOutputFormatter(writerSettings);
var expectedOutput = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>";
// Act
await formatter.WriteAsync(formatterContext);
// Assert
Assert.Same(writerSettings, formatter.WriterSettings);
var responseStream = formatterContext.ActionContext.HttpContext.Response.Body;
Assert.NotNull(responseStream);
responseStream.Position = 0;
var actualOutput = new StreamReader(responseStream, Encoding.UTF8).ReadToEnd();
Assert.Equal(expectedOutput, actualOutput);
}
[Fact] [Fact]
public async Task XmlDataContractSerializerOutputFormatterWritesSimpleTypes() public async Task XmlDataContractSerializerOutputFormatterWritesSimpleTypes()
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlDataContractSerializerOutputFormatter( var formatter = new XmlDataContractSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act
@ -118,8 +156,7 @@ namespace Microsoft.AspNet.Mvc.Core
sampleString = "TestLevelOne string" sampleString = "TestLevelOne string"
} }
}; };
var formatter = new XmlDataContractSerializerOutputFormatter( var formatter = new XmlDataContractSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act
@ -168,8 +205,7 @@ namespace Microsoft.AspNet.Mvc.Core
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType(), var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType(),
"application/xml; charset=utf-16"); "application/xml; charset=utf-16");
var formatter = new XmlDataContractSerializerOutputFormatter( var formatter = new XmlDataContractSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
formatter.WriterSettings.OmitXmlDeclaration = false; formatter.WriterSettings.OmitXmlDeclaration = false;
// Act // Act
@ -190,8 +226,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlDataContractSerializerOutputFormatter( var formatter = new XmlDataContractSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
formatter.WriterSettings.Indent = true; formatter.WriterSettings.Indent = true;
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
@ -213,8 +248,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlDataContractSerializerOutputFormatter( var formatter = new XmlDataContractSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act

View File

@ -66,13 +66,51 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead); Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
} }
[Fact]
public void DefaultConstructor_ExpectedWriterSettings_Created()
{
// Arrange and Act
var formatter = new XmlSerializerOutputFormatter();
// Assert
var writerSettings = formatter.WriterSettings;
Assert.NotNull(writerSettings);
Assert.True(writerSettings.OmitXmlDeclaration);
Assert.False(writerSettings.CloseOutput);
Assert.False(writerSettings.CheckCharacters);
}
[Fact]
public async Task SuppliedWriterSettings_TakeAffect()
{
// Arrange
var writerSettings = FormattingUtilities.GetDefaultXmlWriterSettings();
writerSettings.OmitXmlDeclaration = false;
var sampleInput = new DummyClass { SampleInt = 10 };
var formatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
var formatter = new XmlSerializerOutputFormatter(writerSettings);
var expectedOutput = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<DummyClass xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><SampleInt>10</SampleInt></DummyClass>";
// Act
await formatter.WriteAsync(formatterContext);
// Assert
Assert.Same(writerSettings, formatter.WriterSettings);
var responseStream = formatterContext.ActionContext.HttpContext.Response.Body;
Assert.NotNull(responseStream);
responseStream.Position = 0;
var actualOutput = new StreamReader(responseStream, Encoding.UTF8).ReadToEnd();
Assert.Equal(expectedOutput, actualOutput);
}
[Fact] [Fact]
public async Task XmlSerializerOutputFormatterWritesSimpleTypes() public async Task XmlSerializerOutputFormatterWritesSimpleTypes()
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlSerializerOutputFormatter( var formatter = new XmlSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act
@ -101,8 +139,7 @@ namespace Microsoft.AspNet.Mvc.Core
sampleString = "TestLevelOne string" sampleString = "TestLevelOne string"
} }
}; };
var formatter = new XmlSerializerOutputFormatter( var formatter = new XmlSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act
@ -152,8 +189,7 @@ namespace Microsoft.AspNet.Mvc.Core
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var outputFormatterContext = var outputFormatterContext =
GetOutputFormatterContext(sampleInput, sampleInput.GetType(), "application/xml; charset=utf-16"); GetOutputFormatterContext(sampleInput, sampleInput.GetType(), "application/xml; charset=utf-16");
var formatter = new XmlSerializerOutputFormatter( var formatter = new XmlSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
formatter.WriterSettings.OmitXmlDeclaration = false; formatter.WriterSettings.OmitXmlDeclaration = false;
// Act // Act
@ -174,8 +210,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlSerializerOutputFormatter( var formatter = new XmlSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
formatter.WriterSettings.Indent = true; formatter.WriterSettings.Indent = true;
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
@ -197,8 +232,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var sampleInput = new DummyClass { SampleInt = 10 }; var sampleInput = new DummyClass { SampleInt = 10 };
var formatter = new XmlSerializerOutputFormatter( var formatter = new XmlSerializerOutputFormatter();
XmlOutputFormatter.GetDefaultXmlWriterSettings());
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType()); var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act // Act