diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/SerializableErrorWrapper.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/SerializableErrorWrapper.cs index d247b2fab9..e8a28f576f 100644 --- a/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/SerializableErrorWrapper.cs +++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/SerializableErrorWrapper.cs @@ -14,6 +14,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml [XmlRoot("Error")] public sealed class SerializableErrorWrapper : IXmlSerializable, IUnwrappable { + // Element name used when ModelStateEntry's Key is empty. Dash in element name should avoid collisions with + // other ModelState entries because the character is not legal in an expression name. + private static readonly string EmptyKey = "MVC-Empty"; + // Note: XmlSerializer requires to have default constructor public SerializableErrorWrapper() { @@ -63,6 +67,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml { var key = XmlConvert.DecodeName(reader.LocalName); var value = reader.ReadInnerXml(); + if (string.Equals(EmptyKey, key, StringComparison.Ordinal)) + { + key = string.Empty; + } SerializableError.Add(key, value); reader.MoveToContent(); @@ -81,6 +89,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml { var key = keyValuePair.Key; var value = keyValuePair.Value; + if (string.IsNullOrEmpty(key)) + { + key = EmptyKey; + } + writer.WriteStartElement(XmlConvert.EncodeLocalName(key)); if (value != null) { @@ -102,4 +115,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml return SerializableError; } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.Formatters.Xml.Test/Internal/SerializableErrorWrapperTests.cs b/test/Microsoft.AspNetCore.Mvc.Formatters.Xml.Test/Internal/SerializableErrorWrapperTests.cs index 2a6cfaf47d..ec0175b8df 100644 --- a/test/Microsoft.AspNetCore.Mvc.Formatters.Xml.Test/Internal/SerializableErrorWrapperTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Formatters.Xml.Test/Internal/SerializableErrorWrapperTests.cs @@ -6,7 +6,6 @@ using System.Runtime.Serialization; using System.Text; using System.Xml; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal @@ -28,8 +27,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal public void WrappedSerializableErrorInstance_ReturnedFromProperty() { // Arrange - var serializableError = new SerializableError(); - serializableError.Add("key1", "key1-error"); + var serializableError = new SerializableError + { + { "key1", "key1-error" } + }; // Act var wrapper = new SerializableErrorWrapper(serializableError); @@ -57,7 +58,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal { // Arrange var serializableErrorXml = "" + - "Test Error 1 Test Error 2Test Error 3"; + "Test error 0" + + "Test Error 1 Test Error 2" + + "Test Error 3" + + "Test Error 4"; var serializer = new DataContractSerializer(typeof(SerializableErrorWrapper)); // Act @@ -66,8 +70,28 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal var errors = wrapper.SerializableError; // Assert - Assert.Equal("Test Error 1 Test Error 2", errors["key1"]); - Assert.Equal("Test Error 3", errors["key2"]); + Assert.Collection( + errors, + kvp => + { + Assert.Equal(string.Empty, kvp.Key); + Assert.Equal("Test error 0", kvp.Value); + }, + kvp => + { + Assert.Equal("key1", kvp.Key); + Assert.Equal("Test Error 1 Test Error 2", kvp.Value); + }, + kvp => + { + Assert.Equal("key2", kvp.Key); + Assert.Equal("Test Error 3", kvp.Value); + }, + kvp => + { + Assert.Equal("list[3].key3", kvp.Key); + Assert.Equal("Test Error 4", kvp.Value); + }); } [Fact] @@ -75,11 +99,18 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal { // Arrange var modelState = new ModelStateDictionary(); + modelState.AddModelError(string.Empty, "Test error 0"); modelState.AddModelError("key1", "Test Error 1"); modelState.AddModelError("key1", "Test Error 2"); modelState.AddModelError("key2", "Test Error 3"); + modelState.AddModelError("list[3].key3", "Test Error 4"); var serializableError = new SerializableError(modelState); var outputStream = new MemoryStream(); + var expectedContent = "" + + "Test error 0" + + "Test Error 1 Test Error 2" + + "Test Error 3" + + "Test Error 4"; // Act using (var xmlWriter = XmlWriter.Create(outputStream)) @@ -91,15 +122,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal var res = new StreamReader(outputStream, Encoding.UTF8).ReadToEnd(); // Assert - var expectedContent = - TestPlatformHelper.IsMono ? - "Test Error 1 Test Error 2" + - "Test Error 3" : - "" + - "Test Error 1 Test Error 2Test Error 3"; - Assert.Equal(expectedContent, res); } } -} \ No newline at end of file +}