/// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            // note: the DateTime portion cannot be serialized as a BsonType.DateTime because it is NOT in UTC
            var dateTimeOffset = (DateTimeOffset)value;
            var representationSerializationOptions = EnsureSerializationOptions<RepresentationSerializationOptions>(options);

            switch (representationSerializationOptions.Representation)
            {
                case BsonType.Array:
                    bsonWriter.WriteStartArray();
                    bsonWriter.WriteInt64(dateTimeOffset.Ticks);
                    bsonWriter.WriteInt32((int)dateTimeOffset.Offset.TotalMinutes);
                    bsonWriter.WriteEndArray();
                    break;
                case BsonType.Document:
                    bsonWriter.WriteStartDocument();
                    bsonWriter.WriteDateTime("DateTime", BsonUtils.ToMillisecondsSinceEpoch(dateTimeOffset.UtcDateTime));
                    bsonWriter.WriteInt64("Ticks", dateTimeOffset.Ticks);
                    bsonWriter.WriteInt32("Offset", (int)dateTimeOffset.Offset.TotalMinutes);
                    bsonWriter.WriteEndDocument();
                    break;
                case BsonType.String:
                    bsonWriter.WriteString(XmlConvert.ToString(dateTimeOffset));
                    break;
                default:
                    var message = string.Format("'{0}' is not a valid DateTimeOffset representation.", representationSerializationOptions.Representation);
                    throw new BsonSerializationException(message);
            }
        }
        /// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            if (value == null)
            {
                bsonWriter.WriteNull();
            }
            else
            {
                if (nominalType == typeof(object))
                {
                    var actualType = value.GetType();
                    bsonWriter.WriteStartDocument();
                    bsonWriter.WriteString("_t", TypeNameDiscriminator.GetDiscriminator(actualType));
                    bsonWriter.WriteName("_v");
                    Serialize(bsonWriter, actualType, value, options); // recursive call replacing nominalType with actualType
                    bsonWriter.WriteEndDocument();
                    return;
                }

                var dictionary = (IDictionary)value;
                var dictionarySerializationOptions = EnsureSerializationOptions(options);
                var dictionaryRepresentation = dictionarySerializationOptions.Representation;
                var keyValuePairSerializationOptions = dictionarySerializationOptions.KeyValuePairSerializationOptions;

                if (dictionaryRepresentation == DictionaryRepresentation.Dynamic)
                {
                    dictionaryRepresentation = DictionaryRepresentation.Document;
                    foreach (object key in dictionary.Keys)
                    {
                        var name = key as string; // check for null and type string at the same time
                        if (name == null || name[0] == '$' || name.IndexOf('.') != -1)
                        {
                            dictionaryRepresentation = DictionaryRepresentation.ArrayOfArrays;
                            break;
                        }
                    }
                }

                switch (dictionaryRepresentation)
                {
                    case DictionaryRepresentation.Document:
                        bsonWriter.WriteStartDocument();
                        foreach (DictionaryEntry dictionaryEntry in dictionary)
                        {
                            bsonWriter.WriteName((string)dictionaryEntry.Key);
                            BsonSerializer.Serialize(bsonWriter, typeof(object), dictionaryEntry.Value, keyValuePairSerializationOptions.ValueSerializationOptions);
                        }
                        bsonWriter.WriteEndDocument();
                        break;

                    case DictionaryRepresentation.ArrayOfArrays:
                    case DictionaryRepresentation.ArrayOfDocuments:
                        // override KeyValuePair representation if necessary
                        var keyValuePairRepresentation = (dictionaryRepresentation == DictionaryRepresentation.ArrayOfArrays) ? BsonType.Array : BsonType.Document;
                        if (keyValuePairSerializationOptions.Representation != keyValuePairRepresentation)
                        {
                            keyValuePairSerializationOptions = new KeyValuePairSerializationOptions(
                                keyValuePairRepresentation,
                                keyValuePairSerializationOptions.KeySerializationOptions,
                                keyValuePairSerializationOptions.ValueSerializationOptions);
                        }

                        bsonWriter.WriteStartArray();
                        foreach (DictionaryEntry dictionaryEntry in dictionary)
                        {
                            var keyValuePair = new KeyValuePair<object, object>(dictionaryEntry.Key, dictionaryEntry.Value);
                            _keyValuePairSerializer.Serialize(
                                bsonWriter,
                                typeof(KeyValuePair<object, object>),
                                keyValuePair,
                                keyValuePairSerializationOptions);
                        }
                        bsonWriter.WriteEndArray();
                        break;
                    default:
                        var message = string.Format("'{0}' is not a valid IDictionary representation.", dictionaryRepresentation);
                        throw new BsonSerializationException(message);
                }
            }
        }
        /// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            var decimalValue = (Decimal)value;
            var representationSerializationOptions = EnsureSerializationOptions<RepresentationSerializationOptions>(options);

            switch (representationSerializationOptions.Representation)
            {
                case BsonType.Array:
                    bsonWriter.WriteStartArray();
                    var bits = Decimal.GetBits(decimalValue);
                    bsonWriter.WriteInt32(bits[0]);
                    bsonWriter.WriteInt32(bits[1]);
                    bsonWriter.WriteInt32(bits[2]);
                    bsonWriter.WriteInt32(bits[3]);
                    bsonWriter.WriteEndArray();
                    break;
                case BsonType.Double:
                    bsonWriter.WriteDouble(representationSerializationOptions.ToDouble(decimalValue));
                    break;
                case BsonType.Int32:
                    bsonWriter.WriteInt32(representationSerializationOptions.ToInt32(decimalValue));
                    break;
                case BsonType.Int64:
                    bsonWriter.WriteInt64(representationSerializationOptions.ToInt64(decimalValue));
                    break;
                case BsonType.String:
                    bsonWriter.WriteString(XmlConvert.ToString(decimalValue));
                    break;
                default:
                    var message = string.Format("'{0}' is not a valid Decimal representation.", representationSerializationOptions.Representation);
                    throw new BsonSerializationException(message);
            }
        }
        /// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            if (value == null)
            {
                bsonWriter.WriteNull();
            }
            else
            {
                var actualType = value.GetType();
                var discriminator = GetDiscriminator(nominalType, actualType);
                if (discriminator != null)
                {
                    bsonWriter.WriteStartDocument();
                    bsonWriter.WriteString("_t", discriminator);
                    bsonWriter.WriteName("_v");
                    Serialize(bsonWriter, actualType, value, options);
                    bsonWriter.WriteEndDocument();
                    return;
                }

                var arraySerializationOptions = EnsureSerializationOptions<ArraySerializationOptions>(options);
                var itemSerializationOptions = arraySerializationOptions.ItemSerializationOptions;
                Type lastItemType = null;
                IBsonSerializer lastItemSerializer = null;

                bsonWriter.WriteStartArray();
                foreach (var item in EnumerateItemsInSerializationOrder(value))
                {
                    var itemType = (item == null) ? typeof(object) : item.GetType();
                    IBsonSerializer itemSerializer;
                    if (itemType == lastItemType)
                    {
                        itemSerializer = lastItemSerializer;
                    }
                    else
                    {
                        itemSerializer = BsonSerializer.LookupSerializer(itemType);
                        lastItemType = itemType;
                        lastItemSerializer = itemSerializer;
                    }
                    itemSerializer.Serialize(bsonWriter, typeof(object), item, itemSerializationOptions);
                }
                bsonWriter.WriteEndArray();
            }
        }
        /// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            if (value == null)
            {
                bsonWriter.WriteNull();
            }
            else
            {
                if (nominalType == typeof(object))
                {
                    var actualType = value.GetType();
                    bsonWriter.WriteStartDocument();
                    bsonWriter.WriteString("_t", TypeNameDiscriminator.GetDiscriminator(actualType));
                    bsonWriter.WriteName("_v");
                    Serialize(bsonWriter, actualType, value, options);
                    bsonWriter.WriteEndDocument();
                    return;
                }

                var items = ((Stack)value).ToArray(); // convert to array to allow efficient access in reverse order
                var arraySerializationOptions = EnsureSerializationOptions<ArraySerializationOptions>(options);
                var itemSerializationOptions = arraySerializationOptions.ItemSerializationOptions;

                // serialize first pushed item first (reverse of enumeration order)
                bsonWriter.WriteStartArray();
                for (var i = items.Length - 1; i >= 0; i--)
                {
                    BsonSerializer.Serialize(bsonWriter, typeof(object), items[i], itemSerializationOptions);
                }
                bsonWriter.WriteEndArray();
            }
        }
        /// <summary>
        /// Serializes an object to a BsonWriter.
        /// </summary>
        /// <param name="bsonWriter">The BsonWriter.</param>
        /// <param name="nominalType">The nominal type.</param>
        /// <param name="value">The object.</param>
        /// <param name="options">The serialization options.</param>
        public override void Serialize(
            BsonWriter bsonWriter,
            Type nominalType,
            object value,
            IBsonSerializationOptions options)
        {
            if (value == null)
            {
                bsonWriter.WriteNull();
            }
            else
            {
                if (nominalType == typeof(object))
                {
                    var actualType = value.GetType();
                    bsonWriter.WriteStartDocument();
                    bsonWriter.WriteString("_t", TypeNameDiscriminator.GetDiscriminator(actualType));
                    bsonWriter.WriteName("_v");
                    Serialize(bsonWriter, actualType, value, options);
                    bsonWriter.WriteEndDocument();
                    return;
                }

                var items = (Queue)value;
                var arraySerializationOptions = EnsureSerializationOptions<ArraySerializationOptions>(options);
                var itemSerializationOptions = arraySerializationOptions.ItemSerializationOptions;

                bsonWriter.WriteStartArray();
                foreach (var item in items)
                {
                    BsonSerializer.Serialize(bsonWriter, typeof(object), item, itemSerializationOptions);
                }
                bsonWriter.WriteEndArray();
            }
        }