Search code examples
c#mongodb

How can I save document in mongo in snake case using c#?


How can I save document in mongo in camel case? Now i am trying to save this:

  "parent_template_id": "5aa7822ba6adf805741c5722",
  "type": "Mutable",
  "subject": {
    "workspace_id": "5-DKC0PV8U",
    "additional_data": {
      "boardId": "149018",
    }

but boardId transform into board_id in db (snake case). This is my field in c#:

[BsonElement("additional_data")]
[BsonIgnoreIfNull]
public Dictionary<string, string> AdditionalData { get; set; }

Solution

  • You'll need to register a new serializer that will deal with your senario, thankfully this libary is very extentionable so you'll only need to write a few parts where you need to extend it.

    So to start off with you'll need to create a Serializer to write your string keys as underscore case:

    public class UnderscoreCaseStringSerializer : StringSerializer
    {
        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, string value)
        {
            value = string.Concat(value.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
    
            base.Serialize(context, args, value);
        }
    }
    

    Now we've got a serializer to work with we can register a new dictionary serializer in the bson serializer registry with our new UnderscoreCaseStringSerializer to be used for serialzing the key:

    var customSerializer =
        new DictionaryInterfaceImplementerSerializer<Dictionary<string, string>>
            (DictionaryRepresentation.Document, new UnderscoreCaseStringSerializer(), new ObjectSerializer());
    
    BsonSerializer.RegisterSerializer(customSerializer);
    

    And that's it...

    internal class MyDocument
    {
        public ObjectId Id { get; set; }
    
        public string Name { get; set; }
    
        public Dictionary<string, string> AdditionalData { get; set; }
    }
    
    var collection = new MongoClient().GetDatabase("test").GetCollection<MyDocument>("docs");
    
    var myDocument = new MyDocument
    {
        Name = "test",
        AdditionalData = new Dictionary<string, string>
        {
            ["boardId"] = "149018"
        }
    };
    
    collection.InsertOne(myDocument);
    
    // { "_id" : ObjectId("5b74093fbbbca64ba8ce9d0e"), "name" : "test", "additional_data" : { "board_id" : "149018" } }
    

    You might also want to consider using a ConventionPack to deal with your conventions of having underscores for field names, this just means that you don't need to litter your classes with BsonElement attributes and it will just work by convention.

    public class UnderscoreCaseElementNameConvention : ConventionBase, IMemberMapConvention
    {
        public void Apply(BsonMemberMap memberMap)
        {
            string name = memberMap.MemberName;
            name = string.Concat(name.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
            memberMap.SetElementName(name);
        }
    }
    
    var pack = new ConventionPack();
    pack.Add(new UnderscoreCaseElementNameConvention());
    
    ConventionRegistry.Register(
        "My Custom Conventions",
        pack,
        t => true);