/// <summary>
        /// The <see cref="ToAttributeValue(object,Type,DynamoSerializerOptions)"/> method converts an instance to a DynamoDB attribute value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="targetType">The source value type.</param>
        /// <param name="options">The serialization options.</param>
        /// <returns>A DynamoDB attribute value, or <c>null</c> if the instance state cannot be represented in DynamoDB.</returns>
        public override AttributeValue ToAttributeValue(object value, Type targetType, DynamoSerializerOptions options)
        {
            var mapObject = new Dictionary <string, AttributeValue>();

            foreach (var property in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
            {
                var propertyAttributes = property.GetCustomAttributes();

                // check if this object property should be ignored
                if (!(propertyAttributes.OfType <DynamoPropertyIgnoreAttribute>().SingleOrDefault() is null))
                {
                    continue;
                }

                var propertyValue = property.GetValue(value);
                if (!(propertyValue is null) || !options.IgnoreNullValues)
                {
                    var attributeValue = DynamoSerializer.Serialize(propertyValue, options);
                    if (!(attributeValue is null))
                    {
                        // check if this object property has a custom name
                        var name = propertyAttributes.OfType <DynamoPropertyNameAttribute>().SingleOrDefault()?.Name ?? property.Name;
                        mapObject.Add(name, attributeValue);
                    }
                }
            }
            return(new AttributeValue {
                M = mapObject,
                IsMSet = true
            });
        }
        /// <summary>
        /// The <see cref="ToAttributeValue(object,Type,DynamoSerializerOptions)"/> method converts an instance to a DynamoDB attribute value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="targetType">The source value type.</param>
        /// <param name="options">The serialization options.</param>
        /// <returns>A DynamoDB attribute value, or <c>null</c> if the instance state cannot be represented in DynamoDB.</returns>
        public override AttributeValue ToAttributeValue(object value, Type targetType, DynamoSerializerOptions options)
        {
            var mapDictionary = new Dictionary <string, AttributeValue>();

            foreach (DictionaryEntry?entry in (IDictionary)value)
            {
                if (entry is null)
                {
                    continue;
                }
                var entryValue = entry?.Value;
                if (!(entryValue is null) || !options.IgnoreNullValues)
                {
                    var entryKey = entry?.Key as string;
                    if (entryKey is null)
                    {
                        throw new DynamoSerializationException("null key is not supported");
                    }
                    var attributeValue = DynamoSerializer.Serialize(entryValue, options);
                    if (attributeValue != null)
                    {
                        mapDictionary.Add(entryKey, attributeValue);
                    }
                }
            }
            return(new AttributeValue {
                M = mapDictionary,
                IsMSet = true
            });
        }
        /// <summary>
        /// The <see cref="ToAttributeValue(object,Type,DynamoSerializerOptions)"/> method converts an instance to a DynamoDB attribute value.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="targetType">The source value type.</param>
        /// <param name="options">The serialization options.</param>
        /// <returns>A DynamoDB attribute value, or <c>null</c> if the instance state cannot be represented in DynamoDB.</returns>
        public override AttributeValue ToAttributeValue(object value, Type targetType, DynamoSerializerOptions options)
        {
            var list = new List <AttributeValue>();

            foreach (var item in (IEnumerable)value)
            {
                var attributeValue = DynamoSerializer.Serialize(item, options);
                list.Add(attributeValue ?? new AttributeValue {
                    NULL = true
                });
            }
            return(new AttributeValue {
                L = list,
                IsLSet = true
            });
        }
        internal Dictionary <string, AttributeValue> SerializeItem <TRecord>(TRecord record, DynamoPrimaryKey <TRecord> primaryKey)
            where TRecord : class
        {
            var attributes = DynamoSerializer.Serialize(record, SerializerOptions)?.M;

            if (attributes is null)
            {
                throw new ArgumentException("cannot serialize null record", nameof(record));
            }

            // add type details
            attributes["_t"] = new AttributeValue(Options.GetShortTypeName(typeof(TRecord)));

            // add modified details
            attributes["_m"] = new AttributeValue {
                N = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(CultureInfo.InvariantCulture)
            };

            // add primary key details
            attributes[primaryKey.PKName] = new AttributeValue(primaryKey.PKValue);
            attributes[primaryKey.SKName] = new AttributeValue(primaryKey.SKValue);
            return(attributes);
        }