/// <summary>
        /// Create prototypes for possible values of an entry
        /// </summary>
        private static IEnumerable <Entry> Prototypes(Type memberType, ICustomAttributeProvider customAttributeProvider, ICustomSerialization customSerialization)
        {
            var possibleElementValues = IsCollection(memberType)
                ? customSerialization.PossibleElementValues(memberType, customAttributeProvider)
                : customSerialization.PossibleValues(memberType, customAttributeProvider);
            var validation = customSerialization.CreateValidation(memberType, customAttributeProvider);

            foreach (var prototype in customSerialization.Prototypes(memberType, customAttributeProvider))
            {
                var prototypeEntry = Prototype(prototype, customSerialization);
                prototypeEntry.Validation     = validation;
                prototypeEntry.Value.Possible = possibleElementValues;
                yield return(prototypeEntry);
            }
        }
        /// <summary>
        /// Convert object instance using custom strategy
        /// </summary>
        public static Entry EncodeObject(object instance, ICustomSerialization customSerialization)
        {
            var instanceType = instance.GetType();
            var isValueType  = ValueOrStringType(instanceType);

            var converted = CreateFromType(instanceType, customSerialization);

            // Value or string are easily value encoded
            if (isValueType)
            {
                converted.Value.Current = ConvertToString(instance, customSerialization.FormatProvider);
                return(converted);
            }

            // Collections are encoded as a root entry with subentries
            if (converted.Value.Type == EntryValueType.Collection)
            {
                var possibleElementValues = customSerialization.PossibleElementValues(instanceType, instanceType);
                var strategy = CreateStrategy(instance, instance, instanceType, customSerialization);
                foreach (var entry in strategy.Serialize())
                {
                    entry.Value.Possible = possibleElementValues;
                    converted.SubEntries.Add(entry);
                }
                return(converted);
            }

            // Classes are encoded per-property recursively
            var filtered = customSerialization.GetProperties(instance.GetType());

            foreach (var property in filtered)
            {
                var convertedProperty = EncodeProperty(property, customSerialization);

                object value;
                try
                {
                    value = property.GetValue(instance);
                }
                catch (Exception ex)
                {
                    value = ex;
                    // Change type in case of exception
                    convertedProperty.Value.Type = EntryValueType.Exception;
                }

                switch (convertedProperty.Value.Type)
                {
                case EntryValueType.Collection:
                    // Get collection and iterate if it has entries
                    if (value == null)
                    {
                        break;
                    }

                    var possibleElementValues = customSerialization.PossibleElementValues(property.PropertyType, property);
                    var strategy = CreateStrategy(value, value, property.PropertyType, customSerialization);
                    foreach (var entry in strategy.Serialize())
                    {
                        entry.Value.Possible = possibleElementValues;
                        convertedProperty.SubEntries.Add(entry);
                    }
                    break;

                case EntryValueType.Class:
                    var subEntry = value == null
                            ? EncodeClass(property.PropertyType, customSerialization)
                            : EncodeObject(value, customSerialization);

                    convertedProperty.Value.Current = ConvertToString(subEntry.Value.Current, customSerialization.FormatProvider);
                    convertedProperty.SubEntries    = subEntry.SubEntries;
                    break;

                case EntryValueType.Exception:
                    convertedProperty.Value.Current = ExceptionPrinter.Print((Exception)value);
                    break;

                case EntryValueType.Stream:
                    var stream = value as Stream;
                    if (stream != null)
                    {
                        if (stream.CanSeek)
                        {
                            stream.Seek(0, SeekOrigin.Begin);
                        }

                        convertedProperty.Value.Current = ConvertToBase64(stream);
                    }
                    break;

                default:
                    convertedProperty.Value.Current = ConvertToString(value, customSerialization.FormatProvider);
                    break;
                }

                converted.SubEntries.Add(convertedProperty);
            }

            return(converted);
        }