49

How do I deserialize a BsonDocument object back to the class after getting it from the server?

QueryDocument _document = new QueryDocument("key", "value");
MongoCursor<BsonDocument> _documentsReturned = _collection.FindAs<BsonDocument>(_document);

foreach (BsonDocument _document1 in _documentsReturned)
{
    //deserialize _document1
    //?
}

Do I deserialize using a BsonReader?

Revious
  • 7,816
  • 31
  • 98
  • 147
iefpw
  • 6,816
  • 15
  • 55
  • 79

2 Answers2

98

There are three ways actually:

1.Specify type you want to load directly in FindAs<>

var docs = _collection.FindAs<MyType>(_document);

2.Deserialize document via BsonSerializer:

BsonSerializer.Deserialize<MyType>(doc);

3.Map bson document manually to your class:

var myClass = new Mytype();
myClass.Name = bsonDoc["name"].AsString;

For most cases you are okay with first approach. But sometimes, when your documents is unstructured, you may need third approach.

Andrew Orsich
  • 52,935
  • 16
  • 139
  • 134
  • An error occurred while deserializing the Property1 property of class Class1: ReadString can only be called when CurrentBsonType is String, not when CurrentBsonType is ObjectId. I put the bson objectid to guid of the class and mapped the class. How should I fix this? – iefpw Feb 28 '12 at 19:27
  • @iefpw: Error say that you have different types of Property1 in database and in class (objectId in database and string in class). – Andrew Orsich Feb 28 '12 at 20:33
  • I was trying to deserialize to the wrong class. Add "ObjectId Id { get; set; } to the class and basically everything works without class mapping. – iefpw Feb 29 '12 at 00:27
  • 2
    `FindAs<>()` is not on the interface `IMongoCollection` which is what comes back from `GetCollection()`. Is this an old answer and the method no longer exists or I'm missing something? – PandaWood Jan 13 '22 at 22:36
2

For big applications and which has some structured data, it's recommended to use your custom model while creating and getting the data instead of using BsonDocument.

Creating a model is an important step for deserialization.

Helpful annotations to remember while creating your model:

  • add id property in model. Try to use [BsonId] attribute for good practice:
  • Create a property with annotation as [BsonExtraElements] this will be used to hold any extra elements found during deserialization.
  • you can use [BsonElement] to specify the elementName.
  • [BsonIgnoreIfDefault] - Initializes a new instance of the BsonIgnoreIfDefaultAttribute class

Sample Model structure, where I tried to cover maximum cases. I have created a base class for _id property just for better architecture, but you can use directly in MyModel class as well.

    public abstract class BaseEntity
    {
        // if you'd like to delegate another property to map onto _id then you can decorate it with the BsonIdAttribute, like this
        [BsonId]
        public string id { get; set; }
    }

    public class MyModel: BaseEntity
    {
        [BsonElement("PopulationAsOn")]
        public DateTime? PopulationAsOn { get; set; }
    
        [BsonRepresentation(BsonType.String)]
        [BsonElement("CountryId")]
        public int CountryId { get; set; }

        [Required(AllowEmptyStrings = false)]
        [StringLength(5)]
        [BsonIgnoreIfDefault]
        public virtual string CountryCode { get; set; }

        [BsonIgnoreIfDefault]
        public IList<States> States { get; set; }

        [BsonExtraElements]
        public BsonDocument ExtraElements { get; set; }
    }

Now to Deserialise, directly use your model while calling FindAsync like this:

cursor = await _collection.FindAsync(filter, 
new FindOptions<MyModel, MyModel>() 
{ BatchSize = 1000, Sort = sort }, ct);
KushalSeth
  • 3,265
  • 1
  • 26
  • 29