Add SerializerSettings property to Xml DataContractSerializer input and output formatters.

This commit is contained in:
Anthony Sneed 2015-01-28 23:50:16 +01:00
parent 3e3dd0215d
commit 4821108307
4 changed files with 353 additions and 7 deletions

View File

@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// </summary>
public class XmlDataContractSerializerInputFormatter : IInputFormatter
{
private DataContractSerializerSettings _serializerSettings;
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
/// <summary>
@ -33,6 +34,7 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
_serializerSettings = new DataContractSerializerSettings();
}
/// <inheritdoc />
@ -73,6 +75,24 @@ namespace Microsoft.AspNet.Mvc.Xml
.Any(supportedMediaType => supportedMediaType.IsSubsetOf(requestContentType));
}
/// <summary>
/// Gets or sets the <see cref="DataContractSerializerSettings"/> used to configure the
/// <see cref="DataContractSerializer"/>.
/// </summary>
public DataContractSerializerSettings SerializerSettings
{
get { return _serializerSettings; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_serializerSettings = value;
}
}
/// <summary>
/// Reads the input XML.
/// </summary>
@ -106,7 +126,7 @@ namespace Microsoft.AspNet.Mvc.Xml
/// <returns>The <see cref="XmlObjectSerializer"/> used during deserialization.</returns>
protected virtual XmlObjectSerializer CreateDataContractSerializer(Type type)
{
return new DataContractSerializer(SerializableErrorWrapper.CreateSerializableType(type));
return new DataContractSerializer(SerializableErrorWrapper.CreateSerializableType(type), _serializerSettings);
}
private object GetDefaultValueForType(Type modelType)

View File

@ -16,6 +16,8 @@ namespace Microsoft.AspNet.Mvc.Xml
/// </summary>
public class XmlDataContractSerializerOutputFormatter : OutputFormatter
{
private DataContractSerializerSettings _serializerSettings;
/// <summary>
/// Initializes a new instance of <see cref="XmlDataContractSerializerOutputFormatter"/>
/// with default XmlWriterSettings
@ -38,6 +40,7 @@ namespace Microsoft.AspNet.Mvc.Xml
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
WriterSettings = writerSettings;
_serializerSettings = new DataContractSerializerSettings();
}
/// <summary>
@ -45,6 +48,24 @@ namespace Microsoft.AspNet.Mvc.Xml
/// </summary>
public XmlWriterSettings WriterSettings { get; }
/// <summary>
/// Gets or sets the <see cref="DataContractSerializerSettings"/> used to configure the
/// <see cref="DataContractSerializer"/>.
/// </summary>
public DataContractSerializerSettings SerializerSettings
{
get { return _serializerSettings; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_serializerSettings = value;
}
}
/// <summary>
/// Gets the type of the object to be serialized.
/// </summary>
@ -85,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.Xml
FormattingUtilities.XsdDataContractExporter.GetRootElementName(type);
#endif
// If the serializer does not support this type it will throw an exception.
return new DataContractSerializer(type);
return new DataContractSerializer(type, _serializerSettings);
}
catch (Exception)
{

View File

@ -27,6 +27,13 @@ namespace Microsoft.AspNet.Mvc
public int SampleInt { get; set; }
}
[DataContract(Name = "SomeDummyClass", Namespace = "")]
public class SomeDummyClass : DummyClass
{
[DataMember]
public string SampleString { get; set; }
}
[DataContract(Name = "TestLevelOne", Namespace = "")]
public class TestLevelOne
{
@ -359,6 +366,118 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("Test Error 3", serializableError["key2"]);
}
[Fact]
public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithRootName()
{
// Arrange
var SubstituteRootName = "SomeOtherClass";
var SubstituteRootNamespace = "http://tempuri.org";
var input = string.Format(
"<{0} xmlns=\"{1}\"><SampleInt xmlns=\"\">1</SampleInt></{0}>",
SubstituteRootName,
SubstituteRootNamespace);
var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context));
}
[Fact]
public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithRootName()
{
// Arrange
var expectedInt = 10;
var SubstituteRootName = "SomeOtherClass";
var SubstituteRootNamespace = "http://tempuri.org";
var input = string.Format(
"<{0} xmlns=\"{1}\"><SampleInt xmlns=\"\">{2}</SampleInt></{0}>",
SubstituteRootName,
SubstituteRootNamespace,
expectedInt);
var dictionary = new XmlDictionary();
var settings = new DataContractSerializerSettings
{
RootName = dictionary.Add(SubstituteRootName),
RootNamespace = dictionary.Add(SubstituteRootNamespace)
};
var formatter = new XmlDataContractSerializerInputFormatter
{
SerializerSettings = settings
};
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(model);
var dummyModel = Assert.IsType<DummyClass>(model);
Assert.Equal(expectedInt, dummyModel.SampleInt);
}
[Fact]
public async Task XmlDataContractSerializerFormatterThrowsWhenNotConfiguredWithKnownTypes()
{
// Arrange
var KnownTypeName = "SomeDummyClass";
var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
var input = string.Format(
"<DummyClass i:type=\"{0}\" xmlns:i=\"{1}\"><SampleInt>1</SampleInt>"
+ "<SampleString>Some text</SampleString></DummyClass>",
KnownTypeName,
InstanceNamespace);
var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException), async () => await formatter.ReadAsync(context));
}
[Fact]
public async Task XmlDataContractSerializerFormatterReadsWhenConfiguredWithKnownTypes()
{
// Arrange
var expectedInt = 10;
var expectedString = "TestString";
var KnownTypeName = "SomeDummyClass";
var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
var input = string.Format(
"<DummyClass i:type=\"{0}\" xmlns:i=\"{1}\"><SampleInt>{2}</SampleInt>"
+ "<SampleString>{3}</SampleString></DummyClass>",
KnownTypeName,
InstanceNamespace,
expectedInt,
expectedString);
var settings = new DataContractSerializerSettings
{
KnownTypes = new[] { typeof(SomeDummyClass) }
};
var formatter = new XmlDataContractSerializerInputFormatter
{
SerializerSettings = settings
};
var contentBytes = Encoding.UTF8.GetBytes(input);
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(model);
var dummyModel = Assert.IsType<SomeDummyClass>(model);
Assert.Equal(expectedInt, dummyModel.SampleInt);
Assert.Equal(expectedString, dummyModel.SampleString);
}
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var actionContext = GetActionContext(contentBytes);

View File

@ -7,10 +7,11 @@ using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core.Collections;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNet.Mvc.Xml;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -25,6 +26,13 @@ namespace Microsoft.AspNet.Mvc.Core
public int SampleInt { get; set; }
}
[DataContract(Name = "SomeDummyClass", Namespace = "")]
public class SomeDummyClass : DummyClass
{
[DataMember]
public string SampleString { get; set; }
}
[DataContract(Name = "TestLevelOne", Namespace = "")]
public class TestLevelOne
{
@ -43,6 +51,24 @@ namespace Microsoft.AspNet.Mvc.Core
public TestLevelOne TestOne { get; set; }
}
[DataContract(Name = "Child", Namespace = "")]
public class Child
{
[DataMember]
public int Id { get; set; }
[DataMember]
public Parent Parent { get; set; }
}
[DataContract(Name = "Parent", Namespace = "")]
public class Parent
{
[DataMember]
public string Name { get; set; }
[DataMember]
public List<Child> Children { get; set; }
}
public static IEnumerable<object[]> BasicTypeValues
{
get
@ -194,9 +220,10 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0;
Assert.Equal("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>",
new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd());
"<DummyClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<SampleInt>10</SampleInt></DummyClass>",
new StreamReader(outputFormatterContext.ActionContext.HttpContext.Response.Body,
Encoding.UTF8).ReadToEnd());
}
[Fact]
@ -320,7 +347,8 @@ namespace Microsoft.AspNet.Mvc.Core
[Theory]
[MemberData(nameof(TypesForGetSupportedContentTypes))]
public void XmlDataContractSerializer_GetSupportedContentTypes_Returns_SupportedTypes(Type declaredType, Type runtimeType, object expectedOutput)
public void XmlDataContractSerializer_GetSupportedContentTypes_Returns_SupportedTypes(Type declaredType,
Type runtimeType, object expectedOutput)
{
// Arrange
var formatter = new XmlDataContractSerializerOutputFormatter();
@ -340,6 +368,164 @@ namespace Microsoft.AspNet.Mvc.Core
}
}
[Fact]
public async Task XmlDataContractSerializerOutputFormatterThrowsWhenNotConfiguredWithKnownTypes()
{
// Arrange
var sampleInput = new SomeDummyClass { SampleInt = 1, SampleString = "TestString" };
var formatter = new XmlDataContractSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass));
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}
[Fact]
public async Task XmlDataContractSerializerOutputFormatterThrowsWhenNotConfiguredWithPreserveReferences()
{
// Arrange
var child = new Child { Id = 1 };
var parent = new Parent { Name = "Parent", Children = new List<Child> { child } };
child.Parent = parent;
var formatter = new XmlDataContractSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType());
// Act & Assert
await Assert.ThrowsAsync(typeof(SerializationException),
async () => await formatter.WriteAsync(outputFormatterContext));
}
[Fact]
public async Task XmlDataContractSerializerFormatterWritesWhenConfiguredWithRootName()
{
// Arrange
var sampleInt = 10;
var SubstituteRootName = "SomeOtherClass";
var SubstituteRootNamespace = "http://tempuri.org";
var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
var expectedOutput = string.Format(
"<{0} xmlns:i=\"{2}\" xmlns=\"{1}\"><SampleInt xmlns=\"\">{3}</SampleInt></{0}>",
SubstituteRootName,
SubstituteRootNamespace,
InstanceNamespace,
sampleInt);
var sampleInput = new DummyClass { SampleInt = sampleInt };
var dictionary = new XmlDictionary();
var settings = new DataContractSerializerSettings
{
RootName = dictionary.Add(SubstituteRootName),
RootNamespace = dictionary.Add(SubstituteRootNamespace)
};
var formatter = new XmlDataContractSerializerOutputFormatter
{
SerializerSettings = settings
};
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0;
var actualOutput = new StreamReader(
outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd();
Assert.Equal(expectedOutput, actualOutput);
}
[Fact]
public async Task XmlDataContractSerializerFormatterWritesWhenConfiguredWithKnownTypes()
{
// Arrange
var sampleInt = 10;
var sampleString = "TestString";
var KnownTypeName = "SomeDummyClass";
var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
var expectedOutput = string.Format(
"<DummyClass xmlns:i=\"{1}\" xmlns=\"\" i:type=\"{0}\"><SampleInt>{2}</SampleInt>"
+ "<SampleString>{3}</SampleString></DummyClass>",
KnownTypeName,
InstanceNamespace,
sampleInt,
sampleString);
var sampleInput = new SomeDummyClass
{
SampleInt = sampleInt,
SampleString = sampleString
};
var settings = new DataContractSerializerSettings
{
KnownTypes = new[] { typeof(SomeDummyClass) }
};
var formatter = new XmlDataContractSerializerOutputFormatter
{
SerializerSettings = settings
};
var outputFormatterContext = GetOutputFormatterContext(sampleInput, typeof(DummyClass));
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0;
var actualOutput = new StreamReader(
outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd();
Assert.Equal(expectedOutput, actualOutput);
}
[Fact]
public async Task XmlDataContractSerializerFormatterWritesWhenConfiguredWithPreserveReferences()
{
// Arrange
var sampleId = 1;
var sampleName = "Parent";
var InstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
var SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/";
var expectedOutput = string.Format(
"<Parent xmlns:i=\"{0}\" z:Id=\"{2}\" xmlns:z=\"{1}\">" +
"<Children z:Id=\"2\" z:Size=\"1\">" +
"<Child z:Id=\"3\"><Id>{2}</Id><Parent z:Ref=\"1\" i:nil=\"true\" />" +
"</Child></Children><Name z:Id=\"4\">{3}</Name></Parent>",
InstanceNamespace,
SerializationNamespace,
sampleId,
sampleName);
var child = new Child { Id = sampleId };
var parent = new Parent { Name = sampleName, Children = new List<Child> { child } };
child.Parent = parent;
var settings = new DataContractSerializerSettings
{
PreserveObjectReferences = true
};
var formatter = new XmlDataContractSerializerOutputFormatter
{
SerializerSettings = settings
};
var outputFormatterContext = GetOutputFormatterContext(parent, parent.GetType());
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.NotNull(outputFormatterContext.ActionContext.HttpContext.Response.Body);
outputFormatterContext.ActionContext.HttpContext.Response.Body.Position = 0;
var actualOutput = new StreamReader(
outputFormatterContext.ActionContext.HttpContext.Response.Body, Encoding.UTF8).ReadToEnd();
Assert.Equal(expectedOutput, actualOutput);
}
private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType,
string contentType = "application/xml; charset=utf-8")
{