Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
Referencing this
SO question
about custom serialization of strings to enums and vice versa in Json.NET, decorating the enum members using the EnumMember attribute - is there a way to get MongoDB to perform the same feat?
I have just refactored some previously string fields to enums and was wondering if there is any way to instruct Mongo to also read the EnumMember values when (de-)serializing and avoid me having to go through the database and update all the current text values.
–
i´m using package: PackageReference Include="MongoDB.Bson" Version="2.12.1"
my map class:
public class OfferMap
public static void Configure()
BsonClassMap.RegisterClassMap<Offer>(map => //Offer is a class
map.AutoMap();
map.SetIgnoreExtraElements(true);
.SetIsRootClass(true);
.MapMember(x => x.OfferType)
.SetSerializer(new EnumSerializer<OfferType>(MongoDB.Bson.BsonType.String)) // OfferType is an Enum
.SetElementName("offerType")
.SetIgnoreIfNull(false)
.SetIsRequired(true);
I used a CustomEnumSerializer to handle the EnumMember attribute
public class CustomEnumSerializer<TEnum> : StructSerializerBase<TEnum>, IRepresentationConfigurable<CustomEnumSerializer<TEnum>> where TEnum : struct
private static readonly Dictionary<Type, Dictionary<string, object>> _fromValueMap = new Dictionary<Type, Dictionary<string, object>>(); // string representation to Enum value map
private static readonly Dictionary<Type, Dictionary<object, string>> _toValueMap = new Dictionary<Type, Dictionary<object, string>>(); // Enum value to string map
// private fields
private readonly BsonType _representation;
// constructors
/// <summary>
/// Initializes a new instance of the <see cref="EnumSerializer{TEnum}"/> class.
/// </summary>
public CustomEnumSerializer()
: this((BsonType)0) // 0 means use underlying type
/// <summary>
/// Initializes a new instance of the <see cref="EnumSerializer{TEnum}"/> class.
/// </summary>
/// <param name="representation">The representation.</param>
public CustomEnumSerializer(BsonType representation)
switch (representation)
case 0:
case BsonType.Int32:
case BsonType.Int64:
case BsonType.String:
break;
default:
var message = string.Format("{0} is not a valid representation for an EnumSerializer.", representation);
throw new ArgumentException(message);
// don't know of a way to enforce this at compile time
var enumTypeInfo = typeof(TEnum).GetTypeInfo();
if (!enumTypeInfo.IsEnum)
var message = string.Format("{0} is not an enum type.", typeof(TEnum).FullName);
throw new BsonSerializationException(message);
_representation = representation;
if (representation == BsonType.String)
var enumType = typeof(TEnum);
if (!_fromValueMap.ContainsKey(enumType))
Dictionary<string, object> fromMap = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<object, string> toMap = new Dictionary<object, string>();
FieldInfo[] fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo field in fields)
string name = field.Name;
object enumValue = Enum.Parse(enumType, name);
// use EnumMember attribute if exists
EnumMemberAttribute enumMemberAttrbiute = field.GetCustomAttribute<EnumMemberAttribute>();
if (enumMemberAttrbiute != null)
string enumMemberValue = enumMemberAttrbiute.Value;
fromMap[enumMemberValue] = enumValue;
toMap[enumValue] = enumMemberValue;
toMap[enumValue] = name;
fromMap[name] = enumValue;
_fromValueMap[enumType] = fromMap;
_toValueMap[enumType] = toMap;
// public properties
/// <summary>
/// Gets the representation.
/// </summary>
/// <value>
/// The representation.
/// </value>
public BsonType Representation
get { return _representation; }
// public methods
/// <summary>
/// Deserializes a value.
/// </summary>
/// <param name="context">The deserialization context.</param>
/// <param name="args">The deserialization args.</param>
/// <returns>A deserialized value.</returns>
public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
var bsonReader = context.Reader;
var bsonType = bsonReader.GetCurrentBsonType();
switch (bsonType)
case BsonType.Int32: return (TEnum)Enum.ToObject(typeof(TEnum), bsonReader.ReadInt32());
case BsonType.Int64: return (TEnum)Enum.ToObject(typeof(TEnum), bsonReader.ReadInt64());
case BsonType.Double: return (TEnum)Enum.ToObject(typeof(TEnum), (long)bsonReader.ReadDouble());
case BsonType.String:
var fromValue = FromValue(typeof(TEnum), bsonReader.ReadString());
return (TEnum)Enum.Parse(typeof(TEnum), fromValue.ToString());
default:
throw CreateCannotDeserializeFromBsonTypeException(bsonType);
/// <summary>
/// Serializes a value.
/// </summary>
/// <param name="context">The serialization context.</param>
/// <param name="args">The serialization args.</param>
/// <param name="value">The object.</param>
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnum value)
var bsonWriter = context.Writer;
switch (_representation)
case 0:
var underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum)));
if (underlyingTypeCode == TypeCode.Int64 || underlyingTypeCode == TypeCode.UInt64)
goto case BsonType.Int64;
goto case BsonType.Int32;
case BsonType.Int32:
bsonWriter.WriteInt32(Convert.ToInt32(value));
break;
case BsonType.Int64:
bsonWriter.WriteInt64(Convert.ToInt64(value));
break;
case BsonType.String:
var val = ToValue(typeof(TEnum), value);
bsonWriter.WriteString(val);
break;
default:
throw new BsonInternalException("Unexpected EnumRepresentation.");
private string ToValue(Type enumType, object obj)
Dictionary<object, string> map = _toValueMap[enumType];
return map[obj];
private object FromValue(Type enumType, string value)
Dictionary<string, object> map = _fromValueMap[enumType];
if (!map.ContainsKey(value))
return value;
return map[value];
/// <summary>
/// Returns a serializer that has been reconfigured with the specified representation.
/// </summary>
/// <param name="representation">The representation.</param>
/// <returns>The reconfigured serializer.</returns>
public CustomEnumSerializer<TEnum> WithRepresentation(BsonType representation)
if (representation == _representation)
return this;
return new CustomEnumSerializer<TEnum>(representation);
// explicit interface implementations
IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation)
return WithRepresentation(representation);
I needed a custom deserializer that would return the default value when encountering an unexpected value in the data, rather than the default behavior of throwing a deserialization exception.
public class CustomEnumSerializer<TEnum>: MongoDB.Bson.Serialization.Serializers.EnumSerializer<TEnum>
where TEnum : struct, IComparable, IFormattable, IConvertible
public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
var bsonReader = context.Reader;
var bsonType = bsonReader.GetCurrentBsonType();
var val = "";
switch (bsonType)
case BsonType.String:
val = bsonReader.ReadString() ?? "";
break;
case BsonType.Int32:
val = bsonReader.ReadInt32().ToString();
break;
case BsonType.Int64:
val = bsonReader.ReadInt64().ToString();
break;
case BsonType.Null:
return default(TEnum);
default:
return base.Deserialize(context, args);
if(Enum.TryParse(val, true, out TEnum result)){
return result;
return default(TEnum);
To implement it in your repository:
static MyRepository()
BsonClassMap.RegisterClassMap<MyDataType>(ms =>
ms.AutoMap();
ms.GetMemberMap(i => i.MyEnum)
.SetSerializer(new CustomEnumSerializer<MyEnumType>());
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.