相关文章推荐
闯红灯的冰棍  ·  在Python中使用rbind ...·  1 年前    · 
可爱的领带  ·  android 右对齐 ...·  1 年前    · 
精明的铁链  ·  Qt之JSON教程-使用篇 - ...·  2 年前    · 
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

it'll be stored in MongoDb database as { ... "Gender" : 1 ... }

but i'd prefer something like this { ... "Gender" : "Male" ... }

Is this possible? Custom mapping, reflection tricks, whatever.

My context: I use strongly typed collections over POCO (well, I mark ARs and use polymorphism occasionally). I've got a thin data access abstraction layer in a form of Unit Of Work. So I'm not serializing/deserializing each object but I can (and do) define some ClassMaps. I use official MongoDb driver + fluent-mongodb.

I'd avoid it. The string value takes up way more space than an integer. I would however, if persistence is involved give deterministic values to each item in your enum so Female = 1, Male = 2 so if the enum is added to later or the order of items changed that you don't end up with problems. Deleted Aug 9, 2011 at 13:28 Yes, I was thinking about space but considered it's not a problem since I've got only a few cases where I prefer honest Enums. And you've got it right, future changes is what bothers me. I guess marking every enum values is a viable solution indeed. Thanks Chris! Kostassoid Aug 9, 2011 at 13:37 [JsonConverter(typeof(StringEnumConverter))] // JSON.Net [BsonRepresentation(BsonType.String)] // Mongo public Gender Gender { get; set; } The accepted answer is the better way to go. In general you want your model to be persistence-ignorant. It's arguably no longer a POCO class if it has a MongoDB dependence. Anything that references the model has to reference MongoDB.Bson even if it's not involved in reading/writing. The rules of how to read/write are better contained in the repository layer. Chad Hedgcock Feb 21, 2019 at 16:23 I tend to agree with @ChadHedgcock that it is better to register these types of dependencies at some app- or container-level. However, this answer shows how to override the default / conventional behavior, so I think it's still quite useful. John Gietzen Jul 5, 2020 at 17:30

The MongoDB .NET Driver lets you apply conventions to determine how certain mappings between CLR types and database elements are handled.

If you want this to apply to all your enums, you only have to set up conventions once per AppDomain (usually when starting your application), as opposed to adding attributes to all your types or manually map every type:

// Set up MongoDB conventions
var pack = new ConventionPack
    new EnumRepresentationConvention(BsonType.String)
ConventionRegistry.Register("EnumStringConvention", pack, t => true);
                Documentation on this is less than wonderful. I'd go with the [BsonRepresentation(BsonType.String)] approach on the properties you want to serialize because it's very obvious (as suggested by John Gietzen below, which I'm guessing he got from drdobbs.com/database/mongodb-with-c-deep-dive/240152181?pgno=2 given the example he's used).
– Bart Read
                Nov 10, 2014 at 15:41
                I have found that this is not working for all serialization cases where an enum is used. If in your data structure your enums are boxed in an object type, as it would if you use ExpandoObjects or put values into Dictionary<string, object> to be serialized, the enum values will be serialized as int values even if the convention is there. It seems like the mongodb serialization will work correctly for enum values if the nominal type to serialize is the enum type. But if the nomil type is typeof(Object), serialization to a string is not working.
– sboisse
                Apr 1, 2018 at 12:13

You can customize the class map for the class that contains the enum and specify that the member be represented by a string. This will handle both the serialization and deserialization of the enum.

if (!MongoDB.Bson.Serialization.BsonClassMap.IsClassMapRegistered(typeof(Person)))
        MongoDB.Bson.Serialization.BsonClassMap.RegisterClassMap<Person>(cm =>
           cm.AutoMap();
           cm.GetMemberMap(c => c.Gender).SetRepresentation(BsonType.String);

I am still looking for a way to specify that enums be globally represented as strings, but this is the method that I am currently using.

thanks! that's what i kind of looked for. i can see a certain problems, like more elaborate query model for implementing cqrs, but nothing unmanageable. – Kostassoid Feb 3, 2012 at 14:10 for the Mongo 2.0 driver, this syntax works: BsonSerializer.RegisterSerializer(new EnumSerializer<RealTimeChroState>(BsonType.String)); – BrokeMyLegBiking Aug 26, 2016 at 5:49

I have found that just applying Ricardo Rodriguez' answer is not sufficient in some cases to properly serialize enum values to string into MongoDb:

// Set up MongoDB conventions
var pack = new ConventionPack
    new EnumRepresentationConvention(BsonType.String)
ConventionRegistry.Register("EnumStringConvention", pack, t => true);

If your data structure involves enum values being boxed into objects, the MongoDb serialization will not use the set EnumRepresentationConvention to serialize it.

Indeed, if you look at the implementation of MongoDb driver's ObjectSerializer, it will resolve the TypeCode of the boxed value (Int32 for enum values), and use that type to store your enum value in the database. So boxed enum values end up being serialized as int values. They will remain as int values when being deserialized as well.

To change this, it's possible to write a custom ObjectSerializer that will enforce the set EnumRepresentationConvention if the boxed value is an enum. Something like this:

public class ObjectSerializer : MongoDB.Bson.Serialization.Serializers.ObjectSerializer
     public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
        var bsonWriter = context.Writer;
        if (value != null && value.GetType().IsEnum)
            var conventions = ConventionRegistry.Lookup(value.GetType());
            var enumRepresentationConvention = (EnumRepresentationConvention) conventions.Conventions.FirstOrDefault(convention => convention is EnumRepresentationConvention);
            if (enumRepresentationConvention != null)
                switch (enumRepresentationConvention.Representation)
                    case BsonType.String:
                        value = value.ToString();
                        bsonWriter.WriteString(value.ToString());
                        return;
        base.Serialize(context, args, value);

and then set the custom serializer as the one to use for serializing objects:

BsonSerializer.RegisterSerializer(typeof(object), new ObjectSerializer());

Doing this will ensure boxed enum values will be stored as strings just like the unboxed ones.

Keep in mind however that when deserializing your document, the boxed value will remain a string. It will not be converted back to the original enum value. If you need to convert the string back to the original enum value, a discrimination field will likely have to be added in your document so the serializer can know what is the enum type to desrialize into.

One way to do it would be to store a bson document instead of just a string, into which the discrimination field (_t) and a value field (_v) would be used to store the enum type and its string value.

With driver 2.x I solved using a specific serializer:

BsonClassMap.RegisterClassMap<Person>(cm =>
                cm.AutoMap();
                cm.MapMember(c => c.Gender).SetSerializer(new EnumSerializer<Gender>(BsonType.String));

Use MemberSerializationOptionsConvention to define a convention on how an enum will be saved.

new MemberSerializationOptionsConvention(typeof(Gender), new RepresentationSerializationOptions(BsonType.String))
                I used this solution in the past, but now seems that in 2.x version of the drivers the class MemberSerializationOptionsConvention disappeared. Anyone knows how to obtain the same result with 2.x driver version?
– Alkampfer
                Apr 12, 2016 at 7:31

If you are using .NET Core 3.1 and above, use the latest ultra-fast Json Serializer/Deserializer from Microsoft, System.Text.Json (https://www.nuget.org/packages/System.Text.Json).

See the metrics comparison at https://medium.com/@samichkhachkhi/system-text-json-vs-newtonsoft-json-d01935068143

using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Text.Json.Serialization;;
public class Person
    [JsonConverter(typeof(JsonStringEnumConverter))]  // System.Text.Json.Serialization
    [BsonRepresentation(BsonType.String)]         // MongoDB.Bson.Serialization.Attributes
    public Gender Gender { get; set; }

The answers posted here work well for TEnum and TEnum[], however won't work with Dictionary<TEnum, object>. You could achieve this when initializing serializer using code, however I wanted to do this through attributes. I've created a flexible DictionarySerializer that can be configured with a serializer for the key and value.

public class DictionarySerializer<TDictionary, KeySerializer, ValueSerializer> : DictionarySerializerBase<TDictionary>
    where TDictionary : class, IDictionary, new()
    where KeySerializer : IBsonSerializer, new()
    where ValueSerializer : IBsonSerializer, new()
    public DictionarySerializer() : base(DictionaryRepresentation.Document, new KeySerializer(), new ValueSerializer())
    protected override TDictionary CreateInstance()
        return new TDictionary();
public class EnumStringSerializer<TEnum> : EnumSerializer<TEnum>
    where TEnum : struct
    public EnumStringSerializer() : base(BsonType.String) { }

Usage like this, where both key and value are enum types, but could be any combination of serializers:

    [BsonSerializer(typeof(DictionarySerializer<
        Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum>, 
        EnumStringSerializer<FeatureToggleTypeEnum>,
        EnumStringSerializer<LicenseFeatureStateEnum>>))]
    public Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum> FeatureSettings { get; set; }
        

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.