Avoid `InvalidOperationException` when serializing `SerializableError`
- #8055 - provide unique name (`<Empty-Key>`) for XML elements that would otherwise be nameless nits: - remove now-useless Mono special case in updated test class - extend updated tests to involve square brackets as well as empty keys
This commit is contained in:
parent
f31ab716ee
commit
498fa2d72f
|
|
@ -14,6 +14,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
|
||||||
[XmlRoot("Error")]
|
[XmlRoot("Error")]
|
||||||
public sealed class SerializableErrorWrapper : IXmlSerializable, IUnwrappable
|
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
|
// Note: XmlSerializer requires to have default constructor
|
||||||
public SerializableErrorWrapper()
|
public SerializableErrorWrapper()
|
||||||
{
|
{
|
||||||
|
|
@ -63,6 +67,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
|
||||||
{
|
{
|
||||||
var key = XmlConvert.DecodeName(reader.LocalName);
|
var key = XmlConvert.DecodeName(reader.LocalName);
|
||||||
var value = reader.ReadInnerXml();
|
var value = reader.ReadInnerXml();
|
||||||
|
if (string.Equals(EmptyKey, key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
key = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
SerializableError.Add(key, value);
|
SerializableError.Add(key, value);
|
||||||
reader.MoveToContent();
|
reader.MoveToContent();
|
||||||
|
|
@ -81,6 +89,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
|
||||||
{
|
{
|
||||||
var key = keyValuePair.Key;
|
var key = keyValuePair.Key;
|
||||||
var value = keyValuePair.Value;
|
var value = keyValuePair.Value;
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
key = EmptyKey;
|
||||||
|
}
|
||||||
|
|
||||||
writer.WriteStartElement(XmlConvert.EncodeLocalName(key));
|
writer.WriteStartElement(XmlConvert.EncodeLocalName(key));
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
|
|
@ -102,4 +115,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
|
||||||
return SerializableError;
|
return SerializableError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using System.Runtime.Serialization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Testing;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
||||||
|
|
@ -28,8 +27,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
||||||
public void WrappedSerializableErrorInstance_ReturnedFromProperty()
|
public void WrappedSerializableErrorInstance_ReturnedFromProperty()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var serializableError = new SerializableError();
|
var serializableError = new SerializableError
|
||||||
serializableError.Add("key1", "key1-error");
|
{
|
||||||
|
{ "key1", "key1-error" }
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var wrapper = new SerializableErrorWrapper(serializableError);
|
var wrapper = new SerializableErrorWrapper(serializableError);
|
||||||
|
|
@ -57,7 +58,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var serializableErrorXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
|
var serializableErrorXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
|
||||||
"<Error><key1>Test Error 1 Test Error 2</key1><key2>Test Error 3</key2></Error>";
|
"<Error><MVC-Empty>Test error 0</MVC-Empty>" +
|
||||||
|
"<key1>Test Error 1 Test Error 2</key1>" +
|
||||||
|
"<key2>Test Error 3</key2>" +
|
||||||
|
"<list_x005B_3_x005D_.key3>Test Error 4</list_x005B_3_x005D_.key3></Error>";
|
||||||
var serializer = new DataContractSerializer(typeof(SerializableErrorWrapper));
|
var serializer = new DataContractSerializer(typeof(SerializableErrorWrapper));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -66,8 +70,28 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
||||||
var errors = wrapper.SerializableError;
|
var errors = wrapper.SerializableError;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("Test Error 1 Test Error 2", errors["key1"]);
|
Assert.Collection(
|
||||||
Assert.Equal("Test Error 3", errors["key2"]);
|
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]
|
[Fact]
|
||||||
|
|
@ -75,11 +99,18 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var modelState = new ModelStateDictionary();
|
var modelState = new ModelStateDictionary();
|
||||||
|
modelState.AddModelError(string.Empty, "Test error 0");
|
||||||
modelState.AddModelError("key1", "Test Error 1");
|
modelState.AddModelError("key1", "Test Error 1");
|
||||||
modelState.AddModelError("key1", "Test Error 2");
|
modelState.AddModelError("key1", "Test Error 2");
|
||||||
modelState.AddModelError("key2", "Test Error 3");
|
modelState.AddModelError("key2", "Test Error 3");
|
||||||
|
modelState.AddModelError("list[3].key3", "Test Error 4");
|
||||||
var serializableError = new SerializableError(modelState);
|
var serializableError = new SerializableError(modelState);
|
||||||
var outputStream = new MemoryStream();
|
var outputStream = new MemoryStream();
|
||||||
|
var expectedContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
|
||||||
|
"<Error><MVC-Empty>Test error 0</MVC-Empty>" +
|
||||||
|
"<key1>Test Error 1 Test Error 2</key1>" +
|
||||||
|
"<key2>Test Error 3</key2>" +
|
||||||
|
"<list_x005B_3_x005D_.key3>Test Error 4</list_x005B_3_x005D_.key3></Error>";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
using (var xmlWriter = XmlWriter.Create(outputStream))
|
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();
|
var res = new StreamReader(outputStream, Encoding.UTF8).ReadToEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var expectedContent =
|
|
||||||
TestPlatformHelper.IsMono ?
|
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?><Error xmlns:i=\"" +
|
|
||||||
"http://www.w3.org/2001/XMLSchema-instance\"><key1>Test Error 1 Test Error 2</key1>" +
|
|
||||||
"<key2>Test Error 3</key2></Error>" :
|
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
|
|
||||||
"<Error><key1>Test Error 1 Test Error 2</key1><key2>Test Error 3</key2></Error>";
|
|
||||||
|
|
||||||
Assert.Equal(expectedContent, res);
|
Assert.Equal(expectedContent, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue