Add SerializerSettings property to Xml DataContractSerializer input and output formatters.
This commit is contained in:
parent
3e3dd0215d
commit
4821108307
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue