private IEnumerable <object> DeserializeCollectionValues(XElement serializedCollectionObject, Type collectionType)
        {
            foreach (XElement serializedCollectionItem in serializedCollectionObject.Elements())
            {
                if (serializedCollectionItem.Name.LocalName == "Collection")
                {
                    Type collectionItemType = GetCollectionItemType(collectionType);
                    IEnumerable <object> deserializedCollectionValues = DeserializeCollectionValues(serializedCollectionItem, collectionItemType);

                    ICollectionHelper collectionHelper;
                    if (collectionItemType == typeof(object))
                    {
                        collectionHelper = XmlCollectionHelper.Create(typeof(List <object>));
                    }
                    else
                    {
                        collectionHelper = XmlCollectionHelper.Create(collectionItemType);
                    }
                    collectionHelper.CreateCollectionFromValues(deserializedCollectionValues, out object deserializedCollectionObject);
                    yield return(deserializedCollectionObject);
                }
                else
                {
                    object deserializedObject = DeserializeObject(serializedCollectionItem, null);
                    yield return(deserializedObject);
                }
            }
        }
        /// <summary>
        /// Returns an object that can be used to configure a property of the entity type.
        /// If the specified property is not already part of the model, it will be added.
        /// </summary>
        /// <typeparam name="T">The type of the property to be configured.</typeparam>
        /// <param name="propertyExpression">A lambda expression representing the property to be configured ( blog => blog.Url).</param>
        /// <returns><see cref="XmlPropertyTypeBuilder{TEntity,TProperty}"/>An object that can be used to configure the property.</returns>
        public XmlPropertyTypeBuilder <TEntity, T> Property <T>(Expression <Func <TEntity, T> > propertyExpression)
        {
            ExpressionHelper.GetPropertyInfo(propertyExpression, out string propertyName, out PropertyInfo propertyInfo);
            if (propertyInfo.PropertyType.IsInterface && !XmlCollectionHelper.IsSupportedCollectionType(propertyInfo.PropertyType))
            {
                throw new InvalidOperationException($"Serialization/deserialization from interfaces is not allowd. Property:{propertyInfo.DeclaringType.Name}.{propertyName} of type:{propertyInfo.PropertyType.Name}. Choose a concrete type for selected property.");
            }
            XmlPropertyTypeInfo xmlPropertyTypeInfo = _xmlEntityTypeInfo.GetOrCreatePropertyTypeInfo(propertyName, propertyInfo);

            return(new XmlPropertyTypeBuilder <TEntity, T>(_xmlEntityTypeInfo, xmlPropertyTypeInfo, _xmlModelTypeInfo, _xmlModelTypeBuilder));
        }
        private XElement SerializeObject(object objectToBeSerialized, string objectName)
        {
            if (objectToBeSerialized == null)
            {
                throw new ArgumentNullException(nameof(objectToBeSerialized));
            }
            Type objectToBeSerializedType = objectToBeSerialized.GetType();

            XElement serializedObject = new XElement("Object");

            if (XmlCollectionHelper.IsSupportedCollectionType(objectToBeSerializedType))
            {
                if (objectName == null)
                {
                    objectName = "Collection";
                }
                IEnumerable objectToBeSerializedCollection = (IEnumerable)objectToBeSerialized;
                foreach (object collectionValue in objectToBeSerializedCollection)
                {
                    if (collectionValue != null)
                    {
                        XElement serializedCollectionValue = SerializeObject(collectionValue, null);
                        if (!IsEmptyCollectionNode(serializedCollectionValue))
                        {
                            serializedObject.Add(serializedCollectionValue);
                        }
                    }
                }
            }
            else
            {
                if (objectName == null)
                {
                    objectName = GetSerializationName(objectToBeSerializedType);
                }

                if (XmlDataModel.XmlModelInfo.EntitiesInfoKeyedByType.TryGetValue(objectToBeSerializedType, out XmlEntityTypeInfo xmlEntityTypeInfo))
                {
                    foreach (XmlPropertyTypeInfo xmlPropertyTypeInfo in xmlEntityTypeInfo.PropertiesTypeInfo.Values)
                    {
                        PropertyInfo propertyInfo  = objectToBeSerializedType.GetProperty(xmlPropertyTypeInfo.AssociatedPropertyName);
                        object       propertyValue = propertyInfo.GetValue(objectToBeSerialized, null);

                        if (propertyValue != null)
                        {
                            string propertyName = propertyInfo.Name;
                            if (!string.IsNullOrWhiteSpace(xmlPropertyTypeInfo.XmlName))
                            {
                                propertyName = xmlPropertyTypeInfo.XmlName;
                            }

                            Type propertyValueType = propertyValue.GetType();
                            if (propertyValueType.IsValueType)
                            {
                                if (xmlPropertyTypeInfo.ShouldNotBeSerializedIfHasDefaultValue)
                                {
                                    if (ValueIsEqualWithDefaultValueOfType(propertyValueType, propertyValue))
                                    {
                                        continue;
                                    }
                                }
                            }

                            if (xmlPropertyTypeInfo.SerializeAsXmlAttribute)
                            {
                                propertyValue.Convert(out string serializedValue);
                                XAttribute propertyAttribute = new XAttribute(propertyName, serializedValue);
                                serializedObject.Add(propertyAttribute);
                            }
                            else
                            {
                                XElement serializedProperty = SerializeObject(propertyValue, propertyName);
                                if (!IsEmptyCollectionNode(serializedProperty))
                                {
                                    serializedObject.Add(serializedProperty);
                                }
                            }
                        }
                    }
                }

                if (!ElementIsContainer(serializedObject))
                {
                    objectToBeSerialized.Convert(out string instanceToBeSerializedValue);
                    serializedObject.Value = instanceToBeSerializedValue;
                }
            }

            serializedObject.Name = objectName;
            return(serializedObject);
        }
        private object DeserializeObject(XElement serializedObject, Type serializedObjectType)
        {
            //todo refactor
            if (serializedObjectType == null)
            {
                if (!XmlDataModel.XmlModelInfo.SerializationTypesKeyedByName.TryGetValue(serializedObject.Name.LocalName, out serializedObjectType))
                {
                    string errorMessage = $"Error occurred during xml deserialization. The {serializedObject.Name.LocalName} type cannot be resolved!";
                    throw new SerializationException(errorMessage);
                }
            }

            if (ElementIsContainer(serializedObject))
            {
                if (XmlCollectionHelper.IsSupportedCollectionType(serializedObjectType))
                {
                    IEnumerable <object> deserializedCollectionValues = DeserializeCollectionValues(serializedObject, serializedObjectType);
                    ICollectionHelper    collectionPropertyHelper     = XmlCollectionHelper.Create(serializedObjectType);
                    collectionPropertyHelper.CreateCollectionFromValues(deserializedCollectionValues, out object deserializedCollectionObject);
                    return(deserializedCollectionObject);
                }

                if (XmlDataModel.XmlModelInfo.EntitiesInfoKeyedByType.TryGetValue(serializedObjectType, out XmlEntityTypeInfo xmlEntityTypeInfo))
                {
                    object[] constructorArgs             = null;
                    var      propertiesUsedByConstructor = new Dictionary <string, (object PropertyValue, bool ShouldBeUpdatedBySerializer)>();
                    int      constructorParametersCount  = xmlEntityTypeInfo.ConstructorParametersTypeInfo.Count;
                    if (constructorParametersCount > 0)
                    {
                        constructorArgs = new object[constructorParametersCount];
                        for (int i = 0; i < constructorParametersCount; i++)
                        {
                            XmlConstructorParameterTypeInfo constructorParameterTypeInfo = xmlEntityTypeInfo.ConstructorParametersTypeInfo[i];
                            string associatedPropertyName = constructorParameterTypeInfo.AssociatedPropertyName;
                            if (xmlEntityTypeInfo.PropertiesTypeInfo.TryGetValue(associatedPropertyName, out XmlPropertyTypeInfo xmlPropertyTypeInfo))
                            {
                                string elementName = GetXmlObjectName(xmlPropertyTypeInfo);
                                if (xmlPropertyTypeInfo.SerializeAsXmlAttribute)
                                {
                                    XAttribute serializedPropertyAttribute = serializedObject.Attribute(elementName);
                                    if (serializedPropertyAttribute != null)
                                    {
                                        serializedPropertyAttribute.Value.Convert(xmlPropertyTypeInfo.PropertyInfo.PropertyType, out object convertedValue);
                                        constructorArgs[i] = convertedValue;
                                        propertiesUsedByConstructor[associatedPropertyName] = (convertedValue, constructorParameterTypeInfo.AssociatedPropertyValueIsSetBySerializer);
                                    }
                                    else
                                    {
                                        throw new Exception($"Constructor argument[{i}] of type {serializedObjectType.FullName} is associated with property {associatedPropertyName} but the {elementName} attribute is not present into serialized object body");
                                    }
                                }
                                else
                                {
                                    XElement serializedPropertyElement = serializedObject.Element(elementName);
                                    if (serializedPropertyElement != null)
                                    {
                                        object deserializedObject = DeserializeObject(serializedPropertyElement, xmlPropertyTypeInfo.PropertyInfo.PropertyType);
                                        constructorArgs[i] = deserializedObject;
                                        propertiesUsedByConstructor[associatedPropertyName] = (deserializedObject, constructorParameterTypeInfo.AssociatedPropertyValueIsSetBySerializer);
                                    }
                                    else
                                    {
                                        throw new Exception($"Constructor argument[{i}] of type {serializedObjectType.FullName} is associated with property {associatedPropertyName} but the {elementName} element is not present into serialized object body");
                                    }
                                }
                            }
                            else
                            {
                                throw new Exception($"Constructor argument[{i}] of type {serializedObjectType.FullName} is associated with property {associatedPropertyName} but the property is not configured by model builder.");
                            }
                        }
                    }
                    object instance = Activator.CreateInstance(serializedObjectType, constructorArgs);
                    foreach (XmlPropertyTypeInfo xmlPropertyTypeInfo in xmlEntityTypeInfo.PropertiesTypeInfo.Values)
                    {
                        if (propertiesUsedByConstructor.TryGetValue(xmlPropertyTypeInfo.AssociatedPropertyName, out var constructorProperty))
                        {
                            if (constructorProperty.ShouldBeUpdatedBySerializer)
                            {
                                if (constructorProperty.PropertyValue != null && XmlCollectionHelper.IsSupportedCollectionType(xmlPropertyTypeInfo.PropertyInfo.PropertyType))
                                {
                                    if (xmlPropertyTypeInfo.PropertyInfo.CanRead)
                                    {
                                        object propertyValue = xmlPropertyTypeInfo.PropertyInfo.GetValue(instance, null);
                                        if (propertyValue != null)
                                        {
                                            ICollectionHelper collectionHelper = XmlCollectionHelper.Create(xmlPropertyTypeInfo.PropertyInfo.PropertyType);
                                            if (collectionHelper.TryFillCollectionValues(propertyValue, constructorProperty.PropertyValue))
                                            {
                                                continue;
                                            }
                                        }
                                    }
                                }
                                ThrowIfPropertyCannotWrite(xmlPropertyTypeInfo.PropertyInfo);
                                xmlPropertyTypeInfo.PropertyInfo.SetValue(instance, constructorProperty.PropertyValue);
                            }
                            else
                            {
                                continue;
                            }
                        }

                        if (xmlPropertyTypeInfo.PropertyInfo.DeclaringType != serializedObjectType)
                        {
                            if (XmlDataModel.XmlModelInfo.EntitiesInfoKeyedByType.TryGetValue(xmlPropertyTypeInfo.PropertyInfo.DeclaringType, out XmlEntityTypeInfo baseEntityTypeInfo))
                            {
                                XmlConstructorParameterTypeInfo baseConstructorParameterInfoAssociatedWithCurrentProperty = baseEntityTypeInfo.ConstructorParametersTypeInfo.Values.FirstOrDefault(constructorParameterInfo => constructorParameterInfo.AssociatedPropertyName == xmlPropertyTypeInfo.PropertyInfo.Name);
                                if (baseConstructorParameterInfoAssociatedWithCurrentProperty != null && !baseConstructorParameterInfoAssociatedWithCurrentProperty.AssociatedPropertyValueIsSetBySerializer)
                                {
                                    continue;
                                }
                            }
                        }

                        string elementName = GetXmlObjectName(xmlPropertyTypeInfo);
                        if (xmlPropertyTypeInfo.SerializeAsXmlAttribute)
                        {
                            XAttribute serializedPropertyAttribute = serializedObject.Attribute(elementName);
                            if (serializedPropertyAttribute != null)
                            {
                                ThrowIfPropertyCannotWrite(xmlPropertyTypeInfo.PropertyInfo);
                                serializedPropertyAttribute.Value.Convert(xmlPropertyTypeInfo.PropertyInfo.PropertyType, out object convertedValue);
                                xmlPropertyTypeInfo.PropertyInfo.SetValue(instance, convertedValue);
                            }
                        }
                        else
                        {
                            XElement serializedPropertyElement = serializedObject.Element(elementName);
                            if (serializedPropertyElement != null)
                            {
                                if (XmlCollectionHelper.IsSupportedCollectionType(xmlPropertyTypeInfo.PropertyInfo.PropertyType))
                                {
                                    IEnumerable <object> deserializedCollectionValues = DeserializeCollectionValues(serializedPropertyElement, xmlPropertyTypeInfo.PropertyInfo.PropertyType);
                                    ICollectionHelper    collectionPropertyHelper     = XmlCollectionHelper.Create(xmlPropertyTypeInfo.PropertyInfo.PropertyType);

                                    if (xmlPropertyTypeInfo.PropertyInfo.CanRead)
                                    {
                                        object propertyValue = xmlPropertyTypeInfo.PropertyInfo.GetValue(instance, null);
                                        if (propertyValue != null)
                                        {
                                            ICollectionHelper collectionHelper = XmlCollectionHelper.Create(xmlPropertyTypeInfo.PropertyInfo.PropertyType);
                                            if (collectionHelper.TryFillCollectionValues(propertyValue, deserializedCollectionValues))
                                            {
                                                continue;
                                            }
                                        }
                                    }

                                    ThrowIfPropertyCannotWrite(xmlPropertyTypeInfo.PropertyInfo);
                                    collectionPropertyHelper.CreateCollectionFromValues(deserializedCollectionValues, out object createdCollectionWithValues);
                                    xmlPropertyTypeInfo.PropertyInfo.SetValue(instance, createdCollectionWithValues);
                                    continue;
                                }

                                ThrowIfPropertyCannotWrite(xmlPropertyTypeInfo.PropertyInfo);
                                object deserializedObject = DeserializeObject(serializedPropertyElement, xmlPropertyTypeInfo.PropertyInfo.PropertyType);
                                xmlPropertyTypeInfo.PropertyInfo.SetValue(instance, deserializedObject);
                            }
                        }
                    }
                    return(instance);
                }
                else
                {
                    string errorMessage = $"Error occurred during xml deserialization. Unknown entity {serializedObjectType.FullName}";
                    throw new SerializationException(errorMessage);
                }
            }
            else
            {
                serializedObject.Value.Convert(serializedObjectType, out object convertedValue);
                return(convertedValue);
            }
        }