C# MongoDB BsonDocument not getting serialized to custom class using BsonSerializer

845 views Asked by At

I am having an issue with serializing BsonDocuments using BsonSerializer.

I am using var bsonDoc = collection.Find(...) to pull a single document from a MongoDB database. I then try to serialize it using var strongTypedDocument = BsonSerializer.Deserialize<MainSDocument>(bsonDoc);

Here is the BsonDocument

   { 
      "MainSPayload" : 
        { 
           "GDeets" : { "Id" : 0, "GSerial" : "XX123XX123" }
        }
   }

Here are the C# classes

[JsonObject(MemberSerialization.OptIn)]
[BsonIgnoreExtraElements]
public class MainSDocument
{
    [JsonProperty(Required = Required.Always)]
    public MainState MainSPayload
    {
        get; set;
    }
}

[JsonObject(MemberSerialization.OptIn)]
public class MainState 
{
    [JsonProperty(Required = Required.Always)]
    public GDetails GDeets
    {
        get; set;
    }
}

public class GDetails 
{
    public int Id
    {
        get; set;
    }

    public string GSerial
    {
        get; set;
    }
}

The error I am getting is: FormatException: An error occurred while deserializing the GDetails property of class MainState : Element 'Id' does not match any field or property of class GDetails .'

Why is this happening? Why doesn't the element Id match the int element Id in the GDetails class?

1

There are 1 answers

4
dododo On BEST ANSWER

Serialization for Id field has a special workflow that effectively converts this value into how the server side represents it: _id. To workaround it you can use:

        BsonClassMap.RegisterClassMap<GDetails>(c =>
        {
            c.AutoMap();
            c.UnmapField(u => u.Id); // remove default _id workflow 
            c.MapField(u => u.Id); // add a simple Id field
        });

or simpler:

        BsonClassMap.RegisterClassMap<GDetails>(c =>
        {
            c.AutoMap();
            c.SetIdMember(null);
        });

UPDATE: There is a bit more flexible way. You can configure a new convention and set for what types you will need using it:

        var customPack = new ConventionPack();
        customPack.Add(new NoIdMemberConvention());
        ConventionRegistry.Register("NoIdConvention", customPack, t => t == typeof(GDetails));

also instead the above, you can just add BsonNoId attribute to your class:

    [BsonNoId]
    public class GDetails
    {
        public int Id
        {
            get; set;
        }

        public string GSerial
        {
            get; set;
        }
    }