diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs new file mode 100644 index 0000000..eb0e436 --- /dev/null +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -0,0 +1,155 @@ +using System; +using System.Xml; +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Xml +{ + public abstract class AbstractGenericXmlConverter : AbstractXmlConverter + { + /// + /// Creates a new instance of T. + /// + /// + protected virtual T Instantiate() + { + return ReflectionUtils.CreateInstance(); + } + + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public sealed override void WriteXml(IcdXmlTextWriter writer, string elementName, object value) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + if (value == null) + { + writer.WriteElementString(elementName, null); + return; + } + + WriteXml(writer, elementName, (T)value); + } + + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + [PublicAPI] + public void WriteXml(IcdXmlTextWriter writer, string elementName, T value) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + if (value == null) + { + writer.WriteElementString(elementName, null); + return; + } + + writer.WriteStartElement(elementName); + { + WriteAttributes(writer, value); + WriteElements(writer, value); + } + writer.WriteEndElement(); + } + + /// + /// Override to write attributes to the root element. + /// + /// + /// + protected virtual void WriteAttributes(IcdXmlTextWriter writer, T value) + { + } + + /// + /// Override to write elements to the writer. + /// + /// + /// + protected virtual void WriteElements(IcdXmlTextWriter writer, T value) + { + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + public sealed override object ReadXml(IcdXmlReader reader) + { + return ReadXmlTyped(reader); + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + [PublicAPI] + public virtual T ReadXmlTyped(IcdXmlReader reader) + { + T output = default(T); + bool instantiated = false; + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && reader.IsEmptyElement) + break; + + if (!instantiated) + { + instantiated = true; + output = Instantiate(); + } + + switch (reader.NodeType) + { + case XmlNodeType.Attribute: + ReadAttribute(reader, output); + break; + + case XmlNodeType.Element: + ReadElement(reader, output); + break; + } + } + + return output; + } + + /// + /// Override to handle the current attribute. + /// + /// + /// + protected virtual void ReadAttribute(IcdXmlReader reader, T instance) + { + // Skip the attribute + reader.Read(); + } + + /// + /// Override to handle the current element. + /// + /// + /// + protected virtual void ReadElement(IcdXmlReader reader, T instance) + { + // Skip the element + reader.ReadOuterXml(); + } + } +} diff --git a/ICD.Common.Utils/Xml/AbstractXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractXmlConverter.cs new file mode 100644 index 0000000..23a818c --- /dev/null +++ b/ICD.Common.Utils/Xml/AbstractXmlConverter.cs @@ -0,0 +1,25 @@ +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Xml +{ + public abstract class AbstractXmlConverter : IXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public abstract void WriteXml(IcdXmlTextWriter writer, string elementName, object value); + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + [PublicAPI] + public abstract object ReadXml(IcdXmlReader reader); + } +} diff --git a/ICD.Common.Utils/Xml/DefaultXmlConverter.cs b/ICD.Common.Utils/Xml/DefaultXmlConverter.cs new file mode 100644 index 0000000..da72eea --- /dev/null +++ b/ICD.Common.Utils/Xml/DefaultXmlConverter.cs @@ -0,0 +1,32 @@ +using System; + +namespace ICD.Common.Utils.Xml +{ + public sealed class DefaultXmlConverter : AbstractXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public override void WriteXml(IcdXmlTextWriter writer, string elementName, object value) + { + string elementString = IcdXmlConvert.ToString(value); + + writer.WriteElementString(elementName, elementString); + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + public override object ReadXml(IcdXmlReader reader) + { + throw new NotSupportedException(); + } + } +} diff --git a/ICD.Common.Utils/Xml/IXmlConverter.cs b/ICD.Common.Utils/Xml/IXmlConverter.cs new file mode 100644 index 0000000..e695040 --- /dev/null +++ b/ICD.Common.Utils/Xml/IXmlConverter.cs @@ -0,0 +1,22 @@ +namespace ICD.Common.Utils.Xml +{ + public interface IXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + void WriteXml(IcdXmlTextWriter writer, string elementName, object value); + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + object ReadXml(IcdXmlReader reader); + } +} diff --git a/ICD.Common.Utils/Xml/IcdXmlConvert.cs b/ICD.Common.Utils/Xml/IcdXmlConvert.cs index e9ed1a5..08eed47 100644 --- a/ICD.Common.Utils/Xml/IcdXmlConvert.cs +++ b/ICD.Common.Utils/Xml/IcdXmlConvert.cs @@ -1,4 +1,6 @@ using System; +using System.Text; +using ICD.Common.Utils.IO; #if SIMPLSHARP using Crestron.SimplSharp.CrestronXml; #else @@ -9,6 +11,58 @@ namespace ICD.Common.Utils.Xml { public static class IcdXmlConvert { + /// + /// Serializes the given instance to an xml string. + /// + /// + /// + /// + public static string SerializeObject(string elementName, object value) + { + if (value == null) + return ToString(null); + + IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(value); + + StringBuilder builder = new StringBuilder(); + + using (IcdStringWriter stringWriter = new IcdStringWriter(builder)) + { + using (IcdXmlTextWriter writer = new IcdXmlTextWriter(stringWriter)) + converter.WriteXml(writer, elementName, value); + } + + return builder.ToString(); + } + + /// + /// Deserializes the given xml to an instance of the given type. + /// + /// + /// + /// + public static object DeserializeObject(string xml) + { + return (T)DeserializeObject(typeof(T), xml); + } + + /// + /// Deserializes the given xml to an instance of the given type. + /// + /// + /// + /// + private static object DeserializeObject(Type type, string xml) + { + if (type == null) + throw new ArgumentNullException("type"); + + IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(type); + + using (IcdXmlReader reader = new IcdXmlReader(xml)) + return converter.ReadXml(reader); + } + public static string ToString(int value) { return XmlConvert.ToString(value); diff --git a/ICD.Common.Utils/Xml/XmlConverterAttribute.cs b/ICD.Common.Utils/Xml/XmlConverterAttribute.cs new file mode 100644 index 0000000..af24969 --- /dev/null +++ b/ICD.Common.Utils/Xml/XmlConverterAttribute.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using ICD.Common.Utils.Attributes; + +namespace ICD.Common.Utils.Xml +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public sealed class XmlConverterAttribute : AbstractIcdAttribute + { + private static readonly Dictionary s_InstanceTypeToConverter; + private static readonly Dictionary s_ConverterTypeToConverter; + + private readonly Type m_ConverterType; + + /// + /// Gets the converter type. + /// + public Type ConverterType { get { return m_ConverterType; } } + + /// + /// Static constructor. + /// + static XmlConverterAttribute() + { + s_InstanceTypeToConverter = new Dictionary(); + s_ConverterTypeToConverter = new Dictionary(); + } + + /// + /// Constructor. + /// + /// + public XmlConverterAttribute(Type converterType) + { + m_ConverterType = converterType; + } + + /// + /// Gets the XML converter for the given instance. + /// + /// + /// + public static IXmlConverter GetConverterForInstance(object value) + { + return value == null ? LazyLoadConverter(typeof(DefaultXmlConverter)) : GetConverterForType(value.GetType()); + } + + /// + /// Gets the XML converter for the given type. + /// + /// + /// + public static IXmlConverter GetConverterForType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + IXmlConverter converter; + if (!s_InstanceTypeToConverter.TryGetValue(type, out converter)) + { + XmlConverterAttribute attribute = AttributeUtils.GetClassAttribute(type); + Type converterType = attribute == null ? typeof(DefaultXmlConverter) : attribute.ConverterType; + + converter = LazyLoadConverter(converterType); + s_InstanceTypeToConverter[type] = converter; + } + + return converter; + } + + /// + /// Lazy-loads the converter of the given type. + /// + /// + /// + private static IXmlConverter LazyLoadConverter(Type converterType) + { + if (converterType == null) + throw new ArgumentNullException("converterType"); + + IXmlConverter converter; + if (!s_ConverterTypeToConverter.TryGetValue(converterType, out converter)) + { + converter = ReflectionUtils.CreateInstance(converterType) as IXmlConverter; + s_ConverterTypeToConverter[converterType] = converter; + } + + return converter; + } + } +}