Click here to Skip to main content
11,706,142 members (54,898 online)
Click here to Skip to main content
Articles » Languages » XML » Serializing » Downloads
Add your own
alternative version

Yet Another XML Serialization Library for the .NET Framework

, 2 Oct 2012 MIT 170.4K 269 205
A flexible XML serialization library that lets developers design the XML file structure, and select the exception handling policy. YAXLib supports polymorphic serialization and serializing generic and non-generic collection classes and arrays.
YAXLib-src-1.1.zip
YAXLib
DemoApplication
Properties
Settings.settings
SampleClasses
YAXLib
Properties
YAXLib-src-1.zip
Settings.settings
YAXLib-src-1_1.zip
Settings.settings
YAXLib-src-2.0.zip
Settings.settings
LocalTestRun.testrunconfig
YAXLib.vsmdi
YAXLibTests
Properties
Test References
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Xml;

namespace YAXLib
{
    public class YAXSerializer
    {
        #region Private Members
        private Type m_type;
        private YAXExceptionHandlingPolicies m_exPolicy = YAXExceptionHandlingPolicies.ThrowErrorsOnly;
        private YAXParsingErrors m_parsingErrors = new YAXParsingErrors();
        private YAXExceptionTypes m_defaultExceptionType = YAXExceptionTypes.Warning;

        /// <summary>
        /// Gets the parsing errors.
        /// </summary>
        /// <value>The parsing errors.</value>
        public YAXParsingErrors ParsingErrors
        {
            get
            {
                return m_parsingErrors;
            }
        }

        #endregion

        #region Construction Stuff
        /// <summary>
        /// Initializes a new instance of the <see cref="YAXSerializer"/> class.
        /// </summary>
        /// <param name="t">The type of the object being serialized/deserialized.</param>
        public YAXSerializer(Type t)
            : this(t, YAXExceptionHandlingPolicies.ThrowWarningsAndErrors, YAXExceptionTypes.Error)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="YAXSerializer"/> class.
        /// </summary>
        /// <param name="t">The type of the object being serialized/deserialized.</param>
        /// <param name="exPolicy">The exception handling policy.</param>
        public YAXSerializer(Type t, YAXExceptionHandlingPolicies exPolicy)
            : this(t, exPolicy, YAXExceptionTypes.Error)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="YAXSerializer"/> class.
        /// </summary>
        /// <param name="t">The type of the object being serialized/deserialized.</param>
        /// <param name="exPolicy">The exception handling policy.</param>
        /// <param name="defaultExType">The exceptions are treated as the value specified, unless otherwise specified.</param>
        public YAXSerializer(Type t, YAXExceptionHandlingPolicies exPolicy, YAXExceptionTypes defaultExType)
        {
            this.m_type = t;
            this.m_exPolicy = exPolicy;
            this.m_defaultExceptionType = defaultExType;
        }

        #endregion

        #region Utilities
        /// <summary>
        /// Gets the friendly name for the type. Recommended for generic types.
        /// </summary>
        /// <param name="type">The type to get its friendly name</param>
        /// <returns></returns>
        private static string GetTypeFriendlyName(Type type)
        {
            if (type == null)
                throw new System.ArgumentNullException("type");

            string name = type.Name;
            if (type.IsGenericType)
            {
                int backqIndex = name.IndexOf('`');
                if (backqIndex == 0)
                {
                    throw new InvalidOperationException("Bad type name: " + name);
                }
                else if (backqIndex > 0)
                {
                    name = name.Substring(0, backqIndex);
                }

                name += "Of";

                foreach (Type genType in type.GetGenericArguments())
                {
                    name += GetTypeFriendlyName(genType);
                }
            }

            return name;
        }

        /// <summary>
        /// Determines whether the type specified contains generic parameters or not.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns></returns>
        private static bool TypeContainsGenericParameters(Type type)
        {
            if (type == null)
                throw new System.ArgumentNullException("type");

            if (type.IsGenericType)
            {
                foreach (Type genType in type.GetGenericArguments())
                {
                    if (genType.IsGenericParameter)
                        return true;
                    else if (TypeContainsGenericParameters(genType))
                        return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Gets the type of the items of a collection type.
        /// </summary>
        /// <param name="type">The type of the collection.</param>
        /// <returns>the type of the items of a collection type.</returns>
        private Type GetCollectionItemType(Type type)
        {
            Type itemType = typeof(object);

            if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                itemType = type.GetGenericArguments()[0];
            }
            else if (type.IsInterface && type == typeof(IEnumerable))
            {
                itemType = typeof(object);
            }
            else
            {
                foreach (Type interfaceType in type.GetInterfaces())
                {
                    if (interfaceType.IsGenericType &&
                        interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                    {
                        itemType = interfaceType.GetGenericArguments()[0];
                    }
                }
            }

            return itemType;
        }

        /// <summary>
        /// Determines whether the specified type is basic type. A basic type is one that can be wholly expressed
        /// as an XML attribute. All primitive data types and type <c>string</c> and <c>DataTime</c> are basic.
        /// </summary>
        /// <param name="t">The type</param>
        private static bool IsBasicType(Type t)
        {
            if (t == typeof(string) || t.IsPrimitive || t.IsEnum || t == typeof(DateTime))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Determines whether the specified type is a collection type.
        /// </summary>
        /// <param name="t">The type to check.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is a collection type; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsCollectionType(Type t)
        {
            if (t == typeof(string)) 
                return false;

            return typeof(IEnumerable).IsAssignableFrom(t);
        }

        /// <summary>
        /// Determines whether the specified type is array.
        /// </summary>
        /// <param name="t">The type</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is array; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsArray(Type t)
        {
            return (t.BaseType == typeof(System.Array));
        }

        /// <summary>
        /// Determines whether the specified type is <c>List</c>.
        /// </summary>
        /// <param name="t">The type to check.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is list; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsList(Type t)
        {
            foreach (Type interfaceType in t.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type has implemented the ICollection interface.
        /// </summary>
        /// <param name="t">The type to check.</param>
        /// <param name="itemType">Type of the member items.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type has implemented the ICollection interface; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsICollection(Type t, out Type itemType)
        {
            itemType = typeof(object);

            if (t.IsInterface && t.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                itemType = t.GetGenericArguments()[0];
                return true;
            }

            foreach (Type interfaceType in t.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
                {
                    itemType = interfaceType.GetGenericArguments()[0];
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is dictionary.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="keyType">Type of the key.</param>
        /// <param name="valueType">Type of the value.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is dictionary; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsDictionary(Type type, out Type keyType, out Type valueType)
        {
            keyType = typeof(object);
            valueType = typeof(object);

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                {
                    Type[] genArgs = interfaceType.GetGenericArguments();
                    keyType = genArgs[0];
                    valueType = genArgs[1];
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type has implemented or is an <c>IEnumerable</c> or <c>IEnumerable&lt;&gt;</c> .
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="seqType">Type of the sequence items.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsEnumerable(Type type, out Type seqType)
        {
            seqType = typeof(object);
            bool isNongenericEnumerable = false;

            if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                seqType = type.GetGenericArguments()[0];
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                {
                    Type[] genArgs = interfaceType.GetGenericArguments();
                    seqType = genArgs[0];
                    return true;
                }
                else if(interfaceType == typeof(IEnumerable))
                {
                    isNongenericEnumerable = true;
                }
            }

            if (isNongenericEnumerable)
                return true;

            return false;
        }

        /// <summary>
        /// Called when an exception occurs inside the library. It applies the exception handling policies.
        /// </summary>
        /// <param name="ex">The exception that has occurred.</param>
        /// <param name="exType">Type of the exception.</param>
        private void OnExceptionOccurred(YAXException ex, YAXExceptionTypes exType)
        {
            m_parsingErrors.AddException(ex, exType);
            if ((m_exPolicy == YAXExceptionHandlingPolicies.ThrowWarningsAndErrors) ||
                (m_exPolicy == YAXExceptionHandlingPolicies.ThrowErrorsOnly && exType == YAXExceptionTypes.Error))
                throw ex;
        }

        #endregion

        #region Serialization Stuff

        /// <summary>
        /// Serializes the specified object and returns a string containing the XML.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <returns></returns>
        public string Serialize(object obj)
        {
            return SerializeBase(obj).ToString();
        }

        /// <summary>
        /// Serializes the specified object into a <c>TextWriter</c> instance.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <param name="textWriter">The <c>TextWriter</c> instance.</param>
        public void Serialize(object obj, TextWriter textWriter)
        {
            textWriter.Write(Serialize(obj));
        }

        /// <summary>
        /// Serializes the specified object into a <c>XmlWriter</c> instance.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <param name="xmlWriter">The <c>XmlWriter</c> instance.</param>
        public void Serialize(object obj, XmlWriter xmlWriter)
        {
            SerializeBase(obj).WriteTo(xmlWriter);
        }

        /// <summary>
        /// Serializes the specified object to file.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <param name="fileName">Path to the file.</param>
        public void SerializeToFile(object obj, string fileName)
        {
            string ser = String.Format("{0}\r\n{1}", 
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>", 
                Serialize(obj));
            File.WriteAllText(fileName, ser, Encoding.UTF8);
        }

        private XElement MakeCollectionElement(string elementName, object elementValue, YAXCollectionAttribute collectionAttrInst)
        {
            if (elementValue == null)
                return new XElement(elementName, elementValue);

            if (!(elementValue is IEnumerable))
                throw new ArgumentException("elementValue must be an IEnumerable");

            IEnumerable collectionInst = elementValue as IEnumerable;
            YAXCollectionSerializationTypes serType = YAXCollectionSerializationTypes.Recursive;
            string seperator = "";
            string eachElementName = null;

            if (collectionAttrInst != null)
            {
                serType = collectionAttrInst.SerializationType;
                seperator = collectionAttrInst.SeparateBy;
                eachElementName = collectionAttrInst.EachElementName;
            }

            if (serType == YAXCollectionSerializationTypes.Serially)
            {
                StringBuilder sb = new StringBuilder();

                bool isFirst = true;
                foreach(object obj in collectionInst)
                {
                    if(isFirst)
                    {
                        sb.Append(obj.ToString());
                        isFirst = false;
                    }
                    else
                    {
                        sb.AppendFormat("{0}{1}", seperator, obj.ToString());
                    }
                }

                return MakeElement(elementName, sb.ToString());
            }
            else
            {
                XElement elem = new XElement(elementName, null);

                foreach (object obj in collectionInst)
                {
                    elem.Add(MakeElement(eachElementName ?? GetTypeFriendlyName(obj.GetType()), obj));
                }

                return elem;
            }
        }

        private XElement MakeElement(string name, object value)
        {
            if (value == null || IsBasicType(value.GetType()))
                return new XElement(name, value);
            else
            {
                YAXSerializer ser = new YAXSerializer(value.GetType(), m_exPolicy, m_defaultExceptionType);
                XElement elem = ser.SerializeBase(value, name);
                m_parsingErrors.AddRange(ser.ParsingErrors);
                return elem;
            }
        }

        protected XElement SerializeBase(object obj)
        {
            if (!m_type.IsInstanceOfType(obj))
                throw new YAXObjectTypeMismatch(m_type, obj.GetType());

            string className = GetTypeFriendlyName(obj.GetType());

            foreach (var classAttr in obj.GetType().GetCustomAttributes(true))
            {
                if (classAttr is YAXSerializeAsAttribute)
                {
                    className = (classAttr as YAXSerializeAsAttribute).SerializeAs;
                }
            }

            return SerializeBase(obj, className);
        }

        protected XElement SerializeBase(object obj, string className)
        {
            XElement baseElement = new XElement(
                className, null);

            foreach(var p in obj.GetType().GetProperties())
            {
                string elementName = "";
                object elementValue = null;

                bool dontSerialize = false;
                bool isClassAttr = false;
                bool attributeFor = false;
                bool elementFor = false;
                string parentElement = "";
                bool isCollection = IsCollectionType(p.PropertyType);
                YAXCollectionAttribute collectionAttrInst = null;

                if (p.CanRead)
                {
                    if (p.PropertyType == m_type)
                    {
                        throw new YAXCannotSerializeSelfReferentialTypes(m_type);
                    }

                    elementName = p.Name;
                    elementValue = p.GetValue(obj, null);

                    foreach (var attr in p.GetCustomAttributes(true))
                    {
                        if (attr is YAXSerializeAsAttribute)
                        {
                            elementName = (attr as YAXSerializeAsAttribute).SerializeAs;
                        }
                        else if (attr is YAXDontSerializeAttribute)
                        {
                            dontSerialize = true;
                        }
                        else if (attr is YAXAttributeForClassAttribute)
                        {
                            isClassAttr = IsBasicType(p.PropertyType);
                        }
                        else if (attr is YAXAttributeForAttribute)
                        {
                            attributeFor = IsBasicType(p.PropertyType);
                            parentElement = (attr as YAXAttributeForAttribute).Parent;
                        }
                        else if (attr is YAXElementForAttribute)
                        {
                            elementFor = true;
                            parentElement = (attr as YAXElementForAttribute).Parent;
                        }
                        else if (attr is YAXCollectionAttribute)
                        {
                            collectionAttrInst = attr as YAXCollectionAttribute;
                        }
                    }

                    if (!dontSerialize)
                    {
                        if (isClassAttr)
                        {
                            if (baseElement.Attribute(elementName) == null)
                                baseElement.Add(new XAttribute(elementName, elementValue ?? ""));
                            else
                                throw new YAXAttributeAlreadyExistsException(elementName);
                        }
                        else
                        {
                            if (attributeFor || elementFor)
                            {
                                XElement parElem = baseElement.Element(parentElement);
                                if (parElem == null)
                                {
                                    parElem = new XElement(parentElement);
                                    baseElement.Add(parElem);
                                }

                                if (attributeFor)
                                {
                                    if (parElem.Attribute(elementName) != null)
                                        throw new YAXAttributeAlreadyExistsException(elementName);
                                    else
                                        parElem.Add(new XAttribute(elementName, elementValue ?? ""));
                                }
                                else if (elementFor)
                                {
                                    XElement elemToAdd;
                                    if (isCollection)
                                        elemToAdd = MakeCollectionElement(elementName, elementValue, collectionAttrInst);
                                    else
                                        elemToAdd = MakeElement(elementName, elementValue);

                                    parElem.Add(elemToAdd);
                                }
                            }
                            else
                            {
                                XElement t = baseElement.Element(elementName);
                                if (t == null)
                                {
                                    if (isCollection)
                                        baseElement.Add(MakeCollectionElement(elementName, elementValue, collectionAttrInst));
                                    else
                                        baseElement.Add(MakeElement(elementName, elementValue));
                                }
                                else
                                {
                                    if (IsBasicType(p.PropertyType))
                                    {
                                        t.SetValue(elementValue);
                                    }
                                    else
                                    {
                                        XElement elemToAdd;
                                        if (isCollection)
                                            elemToAdd = MakeCollectionElement(elementName, elementValue, collectionAttrInst);
                                        else
                                            elemToAdd = MakeElement(elementName, elementValue);
                                        
                                        MoveDescendants(elemToAdd, t);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return baseElement;
        }

        private static void MoveDescendants(XElement src, XElement dst)
        {
            foreach (XAttribute attr in src.Attributes())
            {
                if (dst.Attribute(attr.Name) != null)
                {
                    throw new YAXAttributeAlreadyExistsException(attr.Name.ToString());
                }

                dst.Add(attr);
            }

            foreach (XElement elem in src.Elements())
            {
                dst.Add(elem);
            }
        }

        #endregion

        #region Deserialization Stuff
        /// <summary>
        /// Deserializes the specified string containing the XML serialization and returns an object.
        /// </summary>
        /// <param name="input">The input string containing the XML serialization.</param>
        /// <returns></returns>
        public object Deserialize(string input)
        {
            try
            {
                TextReader tr = new StringReader(input);
                XDocument xdoc = XDocument.Load(tr);

                XElement baseElement = xdoc.Root;

                return DeserializeBase(baseElement);
            }
            catch (XmlException ex)
            {
                OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
                return null;
            }
        }

        /// <summary>
        /// Deserializes an object while reading input from an instance of <c>XmlReader</c>.
        /// </summary>
        /// <param name="xmlReader">The <c>XmlReader</c> instance to read input from.</param>
        /// <returns></returns>
        public object Deserialize(XmlReader xmlReader)
        {
            try
            {
                XDocument xdoc = XDocument.Load(xmlReader);
                XElement baseElement = xdoc.Root;
                return DeserializeBase(baseElement);
            }
            catch (XmlException ex)
            {
                OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
                return null;
            }
        }

        /// <summary>
        /// Deserializes an object while reading input from an instance of <c>TextReader</c>.
        /// </summary>
        /// <param name="textReader">The <c>TextReader</c> instance to read input from.</param>
        /// <returns></returns>
        public object Deserialize(TextReader textReader)
        {
            try
            {
                XDocument xdoc = XDocument.Load(textReader);
                XElement baseElement = xdoc.Root;
                return DeserializeBase(baseElement);
            }
            catch (XmlException ex)
            {
                OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
                return null;
            }
        }

        /// <summary>
        /// Deserializes an object from the specified file which contains the XML serialization of the object.
        /// </summary>
        /// <param name="fileName">Path to the file.</param>
        /// <returns></returns>
        public object DeserializeFromFile(string fileName)
        {
            try
            {
                return Deserialize(File.ReadAllText(fileName));
            }
            catch (XmlException ex)
            {
                OnExceptionOccurred(new YAXBadlyFormedXML(ex), m_defaultExceptionType);
                return null;
            }
        }

        private object ConvertBasicType(object value, Type dstType)
        {
            if (!IsBasicType(dstType))
                throw new ArgumentException("Destination type must be a basic type", "dstType");

            object convertedObj = null;
            if (dstType.IsEnum)
                convertedObj = Enum.Parse(dstType, value.ToString());
            else
                convertedObj = Convert.ChangeType(value, dstType);

            return convertedObj;
        }

        private object DeserializeKeyValuePair(XElement baseElement)
        {
            Type[] genArgs = m_type.GetGenericArguments();
            Type keyType = genArgs[0];
            Type valueType = genArgs[1];

            object keyValue, valueValue;
            if (IsBasicType(keyType))
            {
                keyValue = ConvertBasicType(baseElement.Element("Key").Value, keyType);
            }
            else
            {
                YAXSerializer ser = new YAXSerializer(keyType, m_exPolicy, m_defaultExceptionType);
                keyValue = ser.DeserializeBase(baseElement.Element("Key"));
                m_parsingErrors.AddRange(ser.ParsingErrors);
            }

            if (IsBasicType(valueType))
            {
                valueValue = ConvertBasicType(baseElement.Element("Value").Value, valueType);
            }
            else
            {
                YAXSerializer ser = new YAXSerializer(valueType, m_exPolicy, m_defaultExceptionType);
                valueValue = ser.DeserializeBase(baseElement.Element("Value"));
                m_parsingErrors.AddRange(ser.ParsingErrors);
            }

            object pair = m_type.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[] {keyValue, valueValue});

            return pair;
        }

        private object DeserializeBase(XElement baseElement)
        {
            Type t = m_type;

            if (m_type.IsGenericType && m_type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
                return DeserializeKeyValuePair(baseElement);

            object o = t.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);
            foreach (var prop in t.GetProperties())
            {
                if (prop.CanWrite)
                {
                    string propName = prop.Name;
                    string elemValue = "";
                    XElement tElem = null;
                    object convertedObj = null;

                    bool dontSerialize = false;
                    bool isClassAttr = false;
                    bool attributeFor = false;
                    bool elementFor = false;
                    string parentElement = "";
                    bool isCollection = IsCollectionType(prop.PropertyType);
                    YAXExceptionTypes exType = m_defaultExceptionType;
                    object exDefaultValue = null;
                    YAXCollectionAttribute collectionAttrInst = null;
                    bool exOccurred = false;

                    foreach (var propAttr in prop.GetCustomAttributes(true))
                    {
                        if (propAttr is YAXSerializeAsAttribute)
                        {
                            propName = (propAttr as YAXSerializeAsAttribute).SerializeAs;
                        }
                        else if (propAttr is YAXDontSerializeAttribute)
                        {
                            dontSerialize = true;
                        }
                        else if (propAttr is YAXAttributeForClassAttribute)
                        {
                            isClassAttr = IsBasicType( prop.PropertyType );
                        }
                        else if (propAttr is YAXAttributeForAttribute)
                        {
                            attributeFor = IsBasicType(prop.PropertyType);
                            parentElement = (propAttr as YAXAttributeForAttribute).Parent;
                        }
                        else if (propAttr is YAXElementForAttribute)
                        {
                            elementFor = true;
                            parentElement = (propAttr as YAXElementForAttribute).Parent;
                        }
                        else if (propAttr is YAXCollectionAttribute)
                        {
                            collectionAttrInst = propAttr as YAXCollectionAttribute;
                        }
                        else if (propAttr is YAXErrorIfMissedAttribute)
                        {
                            YAXErrorIfMissedAttribute theErrAttr = (propAttr as YAXErrorIfMissedAttribute);
                            exType = theErrAttr.TreatAs;
                            exDefaultValue = theErrAttr.DefaultValue;
                        }
                    }

                    if (!dontSerialize)
                    {
                        if (isClassAttr)
                        {
                            XAttribute tattr = baseElement.Attribute(propName);
                            if (tattr == null)
                            {
                                OnExceptionOccurred(new YAXAttributeMissingException(propName), exType);
                                exOccurred = true;
                            }
                            else
                            {
                                elemValue = tattr.Value;
                            }
                        }
                        else
                        {
                            if (attributeFor || elementFor)
                            {
                                XElement parElem = baseElement.Element(parentElement);
                                if (parElem == null)
                                {
                                    OnExceptionOccurred(new YAXElementMissingException(parentElement), m_defaultExceptionType);
                                    exOccurred = true;
                                }

                                if (attributeFor)
                                {
                                    XAttribute tAttr = parElem.Attribute(propName);
                                    if (tAttr != null)
                                    {
                                        elemValue = tAttr.Value;
                                    }
                                    else
                                    {
                                        OnExceptionOccurred(new YAXAttributeMissingException(propName), exType);
                                        exOccurred = true;
                                    }
                                }
                                else if (elementFor)
                                {
                                    tElem = parElem.Element(propName);
                                    if (tElem != null)
                                    {
                                        elemValue = tElem.Value;
                                    }
                                    else
                                    {
                                        OnExceptionOccurred(new YAXElementMissingException(propName), exType);
                                        exOccurred = true;
                                    }
                                }
                            }
                            else
                            {
                                tElem = baseElement.Element(propName);
                                if (tElem != null)
                                {
                                    elemValue = tElem.Value;
                                }
                                else
                                {
                                    OnExceptionOccurred(new YAXElementMissingException(propName), exType);
                                    exOccurred = true;
                                }
                            }
                        }
                    }

                    // Now try to retrieve elemValue's value
                    if (exOccurred && exDefaultValue != null)
                    {
                        try
                        {
                            prop.SetValue(o, exDefaultValue, null);
                        }
                        catch
                        {
                            OnExceptionOccurred(new YAXDefaultValueCannotBeAssigned(propName, exDefaultValue), m_defaultExceptionType);
                            exOccurred = true;
                        }
                    }
                    else if (elemValue != null)
                    {
                        Type pType = prop.PropertyType;

                        if (pType == typeof(string))
                        {
                            try
                            {
                                prop.SetValue(o, elemValue, null);
                            }
                            catch
                            {
                                OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                exOccurred = true;
                            }
                        }
                        else
                        {
                            if (IsBasicType(pType))
                            {
                                try
                                {
                                    convertedObj = ConvertBasicType(elemValue, pType);
                                    try
                                    {
                                        prop.SetValue(o, convertedObj, null);
                                    }
                                    catch
                                    {
                                        OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                        exOccurred = true;
                                    }
                                }
                                catch(Exception ex)
                                {
                                    if (ex is YAXException)
                                        throw;

                                    OnExceptionOccurred(new YAXBadlyFormedInput(propName, elemValue), exType);
                                    exOccurred = true;

                                    try
                                    {
                                        prop.SetValue(o, exDefaultValue, null);
                                    }
                                    catch
                                    {
                                        OnExceptionOccurred(new YAXDefaultValueCannotBeAssigned(propName, exDefaultValue), m_defaultExceptionType);
                                        exOccurred = true;
                                    }
                                }

                            }
                            else // p's type is not even primitive; what should I do now?
                            {
                                if (isCollection)
                                {
                                    List<object> lst = new List<object>();
                                    Type itemType = GetCollectionItemType(prop.PropertyType);

                                    if (collectionAttrInst != null && collectionAttrInst.SerializationType == YAXCollectionSerializationTypes.Serially)
                                    {
                                        char[] seps = collectionAttrInst.SeparateBy.ToCharArray();
                                        string[] items = elemValue.Split(seps.Union(new char[] { ' ', '\t', '\r', '\n' }).ToArray(), StringSplitOptions.RemoveEmptyEntries);

                                        foreach(string wordItem in items)
                                        {
                                            try
                                            {
                                                lst.Add(ConvertBasicType(wordItem, itemType));
                                            }
                                            catch
                                            {
                                                OnExceptionOccurred(new YAXBadlyFormedInput(propName, elemValue), m_defaultExceptionType);
                                                exOccurred = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        bool isPrimitive = false;
                                        YAXSerializer ser = null;

                                        if (IsBasicType(itemType))
                                        {
                                            isPrimitive = true;
                                        }
                                        else
                                        {
                                            ser = new YAXSerializer(itemType, m_exPolicy, m_defaultExceptionType);
                                        }

                                        string eachElemName = GetTypeFriendlyName(itemType);
                                        if (collectionAttrInst != null && collectionAttrInst.EachElementName != null)
                                            eachElemName = collectionAttrInst.EachElementName;

                                        foreach (XElement childElem in tElem.Elements(eachElemName))
                                        {
                                            if (isPrimitive)
                                            {
                                                try
                                                {
                                                    lst.Add(ConvertBasicType(childElem.Value, itemType));
                                                }
                                                catch
                                                {
                                                    OnExceptionOccurred(new YAXBadlyFormedInput(childElem.Name.ToString(), childElem.Value), m_defaultExceptionType);
                                                    exOccurred = true;
                                                }
                                            }
                                            else
                                            {
                                                lst.Add(ser.DeserializeBase(childElem));
                                            }
                                        }

                                        if(ser != null)
                                            m_parsingErrors.AddRange(ser.ParsingErrors);
                                    }

                                    // Now what should I do with the filled list: lst
                                    Type dicKeyType, dicValueType, enumType;
                                    Type auxColTypeToCreate;
                                    Type colTypeToCreate;
                                    Type[] typeArgs;
                                    if (IsArray(prop.PropertyType))
                                    {
                                        #region The collection is array
                                        auxColTypeToCreate = typeof(List<>);
                                        typeArgs = new Type[] { itemType };
                                        colTypeToCreate = auxColTypeToCreate.MakeGenericType(typeArgs);
                                        object genArObj = Activator.CreateInstance(colTypeToCreate);

                                        foreach (object lstItem in lst)
                                        {
                                            try
                                            {
                                                colTypeToCreate.InvokeMember("Add", BindingFlags.InvokeMethod, null, genArObj, new object[] { lstItem });
                                            }
                                            catch
                                            {
                                                OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
                                                exOccurred = true;
                                            }
                                        }

                                        try
                                        {
                                            prop.SetValue(o,
                                                colTypeToCreate.InvokeMember("ToArray", BindingFlags.InvokeMethod, null, genArObj, new object[0]),
                                                null);
                                        }
                                        catch
                                        {
                                            OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                            exOccurred = true;
                                        }
                                        #endregion
                                    }
                                    else if (IsDictionary(prop.PropertyType, out dicKeyType, out dicValueType))
                                    {
                                        #region The collection is a Dictionary
                                        object dic = prop.PropertyType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);

                                        object key, value;
                                        foreach (object lstItem in lst)
                                        {
                                            key = itemType.GetProperty("Key").GetValue(lstItem, null);
                                            value = itemType.GetProperty("Value").GetValue(lstItem, null);

                                            try
                                            {
                                                prop.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, dic, new object[] { key, value });
                                            }
                                            catch
                                            {
                                                OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
                                                exOccurred = true;
                                            }
                                        }

                                        try
                                        {
                                            prop.SetValue(o, dic, null);
                                        }
                                        catch
                                        {
                                            OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                            exOccurred = true;
                                        }
                                        #endregion
                                    }
                                    else if (IsICollection(prop.PropertyType, out enumType))
                                    {
                                        #region The collection is an ICollection e.g. HashSet
                                        object col = prop.PropertyType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, null, new object[0]);

                                        foreach (object lstItem in lst)
                                        {
                                            try
                                            {
                                                prop.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, col, new object[] { lstItem });
                                            }
                                            catch
                                            {
                                                OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
                                                exOccurred = true;

                                            }
                                        }

                                        try
                                        {
                                            prop.SetValue(o, col, null);
                                        }
                                        catch
                                        {
                                            OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                            exOccurred = true;

                                        }
                                        #endregion
                                    }
                                    else if (IsEnumerable(prop.PropertyType, out enumType)) // this MUST always be the last "else if"
                                    {
                                        #region The collection is an IEnumerable
                                        auxColTypeToCreate = typeof(List<>);
                                        typeArgs = new Type[] { enumType };
                                        colTypeToCreate = auxColTypeToCreate.MakeGenericType(typeArgs);
                                        object genArObj = Activator.CreateInstance(colTypeToCreate);

                                        foreach (object lstItem in lst)
                                        {
                                            try
                                            {
                                                colTypeToCreate.InvokeMember("Add", BindingFlags.InvokeMethod, null, genArObj, new object[] { lstItem });
                                            }
                                            catch
                                            {
                                                OnExceptionOccurred(new YAXCannotAddObjectToCollection(propName, lstItem), m_defaultExceptionType);
                                                exOccurred = true;

                                            }
                                        }

                                        try
                                        {
                                            prop.SetValue(o, genArObj, null);
                                        }
                                        catch
                                        {
                                            OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                            exOccurred = true;

                                        }
                                        #endregion
                                    }
                                }
                                else
                                {
                                    YAXSerializer ser = new YAXSerializer(prop.PropertyType, m_exPolicy, m_defaultExceptionType);
                                    convertedObj = ser.DeserializeBase(tElem);
                                    m_parsingErrors.AddRange(ser.ParsingErrors);

                                    try
                                    {
                                        prop.SetValue(o, convertedObj, null);
                                    }
                                    catch
                                    {
                                        OnExceptionOccurred(new YAXPropertyCannotBeAssignedTo(propName), m_defaultExceptionType);
                                        exOccurred = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return o;
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Sina Iravanian
Software Developer
Australia Australia
A software designer and developer

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150819.1 | Last Updated 2 Oct 2012
Article Copyright 2009 by Sina Iravanian
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid