fix: Fixed a bug where AbstractGenericXmlConverter was not reading out of empty elements

This commit is contained in:
Chris Cameron
2021-05-04 17:26:01 -04:00
parent ae10abd71e
commit b3c1daaab5
2 changed files with 170 additions and 30 deletions

View File

@@ -0,0 +1,155 @@
using System.Xml;
using ICD.Common.Utils.Xml;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Xml
{
[TestFixture]
public sealed class GenericXmlConverterTest
{
[XmlConverter(typeof(TestClassConverter))]
private sealed class TestClass
{
public string A { get; set; }
public int B { get; set; }
}
private sealed class TestClassConverter : AbstractGenericXmlConverter<TestClass>
{
private const string ELEMENT_A = "A";
private const string ATTRIBUTE_B = "b";
/// <summary>
/// Override to handle the current attribute.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected override void ReadAttribute(IcdXmlReader reader, TestClass instance)
{
switch (reader.Name)
{
case ATTRIBUTE_B:
instance.B = int.Parse(reader.Value);
break;
default:
base.ReadAttribute(reader, instance);
break;
}
}
/// <summary>
/// Override to handle the current element.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected override void ReadElement(IcdXmlReader reader, TestClass instance)
{
switch (reader.Name)
{
case ELEMENT_A:
instance.A = reader.ReadElementContentAsString();
break;
default:
base.ReadElement(reader, instance);
break;
}
}
/// <summary>
/// Override to write attributes to the root element.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected override void WriteAttributes(IcdXmlTextWriter writer, TestClass value)
{
base.WriteAttributes(writer, value);
writer.WriteAttributeString(ATTRIBUTE_B, value.B.ToString());
}
/// <summary>
/// Override to write elements to the writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected override void WriteElements(IcdXmlTextWriter writer, TestClass value)
{
base.WriteElements(writer, value);
writer.WriteElementString(ELEMENT_A, value.A);
}
}
[Test]
public void TestElement()
{
const string xml = @"<Instances>
<Instance b=""1"">
<A>Test</A>
</Instance>
</Instances>";
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
// Read into the Instances node
reader.Read();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
// Read into the Instance node
reader.Read();
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instance", reader.Name);
// Deserialize
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
Assert.IsNotNull(instance);
Assert.AreEqual("Test", instance.A);
Assert.AreEqual(1, instance.B);
// Deserialization should land on the following node
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
}
}
[Test]
public void TestEmptyElement()
{
const string xml = @"<Instances>
<Instance b=""1"" />
</Instances>";
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
// Read into the Instances node
reader.Read();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
// Read into the Instance node
reader.Read();
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instance", reader.Name);
// Deserialize
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
Assert.IsNotNull(instance);
Assert.IsNull(instance.A);
Assert.AreEqual(1, instance.B);
// Deserialization should land on the following node
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
}
}
}
}

View File

@@ -111,39 +111,24 @@ namespace ICD.Common.Utils.Xml
if (reader.NodeType != XmlNodeType.Element && !reader.ReadToNextElement())
throw new FormatException();
T output = default(T);
bool instantiated = false;
bool isEmpty = reader.IsEmptyElement;
T output = Instantiate();
// Read the root attributes
while (reader.MoveToNextAttribute())
ReadAttribute(reader, output);
// Read out of the root element
if (!reader.Read())
throw new FormatException();
// There were no child elements
if (isEmpty)
return output;
// Read through child elements
while (true)
{
if (!instantiated)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.IsEmptyElement)
return default(T);
output = Instantiate();
instantiated = true;
// Read the root attributes
while (reader.MoveToNextAttribute())
ReadAttribute(reader, output);
// Read out of the root element
if (!reader.Read())
throw new FormatException();
continue;
default:
// Keep reading until we reach the root element.
if (!reader.Read())
throw new FormatException();
continue;
}
}
switch (reader.NodeType)
{
case XmlNodeType.Element: