[Fixes #6197] AddXmlSerializerFormatters with no namespace

This commit is contained in:
Kiran Challa 2017-12-01 08:58:33 -08:00
parent 05d02e7cab
commit d5bb73a98c
5 changed files with 219 additions and 36 deletions

View File

@ -203,6 +203,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
return XmlWriter.Create(writer, xmlWriterSettings);
}
/// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given <see cref="TextWriter"/> and
/// <see cref="XmlWriterSettings"/>.
/// </summary>
/// <param name="context">The formatter context associated with the call.</param>
/// <param name="writer">
/// The underlying <see cref="TextWriter"/> which the <see cref="XmlWriter"/> should write to.
/// </param>
/// <param name="xmlWriterSettings">
/// The <see cref="XmlWriterSettings"/>.
/// </param>
/// <returns>A new instance of <see cref="XmlWriter"/>.</returns>
public virtual XmlWriter CreateXmlWriter(
OutputFormatterWriteContext context,
TextWriter writer,
XmlWriterSettings xmlWriterSettings)
{
return CreateXmlWriter(writer, xmlWriterSettings);
}
/// <inheritdoc />
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
@ -235,7 +255,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
{
using (var xmlWriter = CreateXmlWriter(textWriter, writerSettings))
using (var xmlWriter = CreateXmlWriter(context, textWriter, writerSettings))
{
dataContractSerializer.WriteObject(xmlWriter, value);
}

View File

@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// <param name="xmlWriterSettings">
/// The <see cref="XmlWriterSettings"/>.
/// </param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns>
/// <returns>A new instance of <see cref="XmlWriter"/>.</returns>
public virtual XmlWriter CreateXmlWriter(
TextWriter writer,
XmlWriterSettings xmlWriterSettings)
@ -179,6 +179,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
return XmlWriter.Create(writer, xmlWriterSettings);
}
/// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given <see cref="TextWriter"/> and
/// <see cref="XmlWriterSettings"/>.
/// </summary>
/// <param name="context">The formatter context associated with the call.</param>
/// <param name="writer">
/// The underlying <see cref="TextWriter"/> which the <see cref="XmlWriter"/> should write to.
/// </param>
/// <param name="xmlWriterSettings">
/// The <see cref="XmlWriterSettings"/>.
/// </param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter(
OutputFormatterWriteContext context,
TextWriter writer,
XmlWriterSettings xmlWriterSettings)
{
return CreateXmlWriter(writer, xmlWriterSettings);
}
/// <inheritdoc />
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
@ -211,9 +231,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
{
using (var xmlWriter = CreateXmlWriter(textWriter, writerSettings))
using (var xmlWriter = CreateXmlWriter(context, textWriter, writerSettings))
{
xmlSerializer.Serialize(xmlWriter, value);
Serialize(xmlSerializer, xmlWriter, value);
}
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
@ -223,6 +243,18 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
}
/// <summary>
/// Serializes value using the passed in <paramref name="xmlSerializer"/> and <paramref name="xmlWriter"/>.
/// </summary>
/// <param name="xmlSerializer">The serializer used to serialize the <paramref name="value"/>.</param>
/// <param name="xmlWriter">The writer used by the serializer <paramref name="xmlSerializer"/>
/// to serialize the <paramref name="value"/>.</param>
/// <param name="value">The value to be serialized.</param>
protected virtual void Serialize(XmlSerializer xmlSerializer, XmlWriter xmlWriter, object value)
{
xmlSerializer.Serialize(xmlWriter, value);
}
/// <summary>
/// Gets the cached serializer or creates and caches the serializer for the given type.
/// </summary>

View File

@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation. 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.Threading;
using System.Threading.Tasks;
using Moq;
namespace Microsoft.AspNetCore.Mvc
@ -11,11 +13,20 @@ namespace Microsoft.AspNetCore.Mvc
{
public static Stream GetThrowingStream()
{
var mock = new Mock<Stream>();
mock.Verify(m => m.Flush(), Times.Never());
mock.Verify(m => m.FlushAsync(It.IsAny<CancellationToken>()), Times.Never());
return new NonFlushingStream();
}
return mock.Object;
private class NonFlushingStream : MemoryStream
{
public override void Flush()
{
throw new InvalidOperationException("Flush should not have been called.");
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
throw new InvalidOperationException("FlushAsync should not have been called.");
}
}
}
}

View File

@ -682,6 +682,42 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Assert.False(canWriteResult);
}
public static TheoryData<bool, object, string> CanIndentOutputConditionallyData
{
get
{
var obj = new DummyClass { SampleInt = 10 };
var newLine = Environment.NewLine;
return new TheoryData<bool, object, string>()
{
{ true, obj, "<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
$"{newLine} <SampleInt>10</SampleInt>{newLine}</DummyClass>" },
{ false, obj, "<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>" }
};
}
}
[Theory]
[MemberData(nameof(CanIndentOutputConditionallyData))]
public async Task CanIndentOutputConditionally(bool indent, object input, string expectedOutput)
{
// Arrange
var formatter = new IndentingXmlDataContractSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(input, input.GetType());
outputFormatterContext.HttpContext.Request.QueryString = new QueryString("?indent=" + indent);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
var body = outputFormatterContext.HttpContext.Response.Body;
body.Position = 0;
var content = new StreamReader(body).ReadToEnd();
Assert.Equal(expectedOutput, content);
}
private OutputFormatterWriteContext GetOutputFormatterContext(
object outputValue,
Type outputType,
@ -696,20 +732,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
private static HttpContext GetHttpContext(string contentType)
{
var request = new Mock<HttpRequest>();
var headers = new HeaderDictionary();
headers["Accept-Charset"] = MediaTypeHeaderValue.Parse(contentType).Charset.ToString();
request.Setup(r => r.ContentType).Returns(contentType);
request.SetupGet(r => r.Headers).Returns(headers);
var response = new Mock<HttpResponse>();
response.SetupGet(f => f.Body).Returns(new MemoryStream());
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.Response).Returns(response.Object);
return httpContext.Object;
var httpContext = new DefaultHttpContext();
var request = httpContext.Request;
request.Headers["Accept-Charset"] = MediaTypeHeaderValue.Parse(contentType).Charset.ToString();
request.ContentType = contentType;
httpContext.Response.Body = new MemoryStream();
return httpContext;
}
private class TestXmlDataContractSerializerOutputFormatter : XmlDataContractSerializerOutputFormatter
@ -723,6 +751,22 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
}
}
private class IndentingXmlDataContractSerializerOutputFormatter : XmlDataContractSerializerOutputFormatter
{
public override XmlWriter CreateXmlWriter(
OutputFormatterWriteContext context,
TextWriter writer,
XmlWriterSettings xmlWriterSettings)
{
var request = context.HttpContext.Request;
if (request.Query["indent"] == "True")
{
xmlWriterSettings.Indent = true;
}
return base.CreateXmlWriter(context, writer, xmlWriterSettings);
}
}
public class Customer
{
public Customer(int id)

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
@ -71,6 +72,62 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
XmlAssert.Equal(expectedOutput, content);
}
public static TheoryData<bool, object, string> CanIndentOutputConditionallyData
{
get
{
var obj = new DummyClass { SampleInt = 10 };
var newLine = Environment.NewLine;
return new TheoryData<bool, object, string>()
{
{ true, obj, "<DummyClass xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
$"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">{newLine} <SampleInt>10</SampleInt>{newLine}</DummyClass>" },
{ false, obj, "<DummyClass xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><SampleInt>10</SampleInt></DummyClass>" }
};
}
}
[Theory]
[MemberData(nameof(CanIndentOutputConditionallyData))]
public async Task XmlSerializer_CanIndentOutputConditionally(bool indent, object input, string expectedOutput)
{
// Arrange
var formatter = new IndentingXmlSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(input, input.GetType());
outputFormatterContext.HttpContext.Request.QueryString = new QueryString("?indent=" + indent);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
var body = outputFormatterContext.HttpContext.Response.Body;
body.Position = 0;
var content = new StreamReader(body).ReadToEnd();
Assert.Equal(expectedOutput, content);
}
[Fact]
public async Task XmlSerializer_CanModifyNamespacesInGeneratedXml()
{
// Arrange
var input = new DummyClass { SampleInt = 10 };
var formatter = new IgnoreAmbientNamespacesXmlSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(input, input.GetType());
var expectedOutput = "<DummyClass><SampleInt>10</SampleInt></DummyClass>";
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
var body = outputFormatterContext.HttpContext.Response.Body;
body.Position = 0;
var content = new StreamReader(body).ReadToEnd();
Assert.Equal(expectedOutput, content);
}
[Fact]
public void XmlSerializer_CachesSerializerForType()
{
@ -461,20 +518,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
private static HttpContext GetHttpContext(string contentType)
{
var request = new Mock<HttpRequest>();
var headers = new HeaderDictionary();
headers["Accept-Charset"] = MediaTypeHeaderValue.Parse(contentType).Charset.ToString();
request.Setup(r => r.ContentType).Returns(contentType);
request.SetupGet(r => r.Headers).Returns(headers);
var response = new Mock<HttpResponse>();
response.SetupGet(f => f.Body).Returns(new MemoryStream());
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.Response).Returns(response.Object);
return httpContext.Object;
var httpContext = new DefaultHttpContext();
var request = httpContext.Request;
request.Headers["Accept-Charset"] = MediaTypeHeaderValue.Parse(contentType).Charset.ToString();
request.ContentType = contentType;
httpContext.Response.Body = new MemoryStream();
return httpContext;
}
private class TestXmlSerializerOutputFormatter : XmlSerializerOutputFormatter
@ -496,5 +545,32 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public int MyProperty { get; set; }
}
private class IndentingXmlSerializerOutputFormatter : XmlSerializerOutputFormatter
{
public override XmlWriter CreateXmlWriter(
OutputFormatterWriteContext context,
TextWriter writer,
XmlWriterSettings xmlWriterSettings)
{
var request = context.HttpContext.Request;
if (request.Query["indent"] == "True")
{
xmlWriterSettings.Indent = true;
}
return base.CreateXmlWriter(context, writer, xmlWriterSettings);
}
}
private class IgnoreAmbientNamespacesXmlSerializerOutputFormatter : XmlSerializerOutputFormatter
{
protected override void Serialize(XmlSerializer xmlSerializer, XmlWriter xmlWriter, object value)
{
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
xmlSerializer.Serialize(xmlWriter, value, namespaces);
}
}
}
}