diff --git a/ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs b/ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs new file mode 100644 index 0000000..c38ddb9 --- /dev/null +++ b/ICD.Common.Utils.Tests/Xml/GenericXmlConverterTest.cs @@ -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 + { + private const string ELEMENT_A = "A"; + private const string ATTRIBUTE_B = "b"; + + /// + /// Override to handle the current attribute. + /// + /// + /// + 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; + } + } + + /// + /// Override to handle the current element. + /// + /// + /// + 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; + } + } + + /// + /// Override to write attributes to the root element. + /// + /// + /// + protected override void WriteAttributes(IcdXmlTextWriter writer, TestClass value) + { + base.WriteAttributes(writer, value); + + writer.WriteAttributeString(ATTRIBUTE_B, value.B.ToString()); + } + + /// + /// Override to write elements to the writer. + /// + /// + /// + 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 = @" + + Test + +"; + + 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(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 = @" + +"; + + 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(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); + } + } + } +} diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs index 287ba0f..fe4ba1a 100644 --- a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -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: