Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Unexpected element: XX' during deserialization MongoDB C#

I'm trying to persist an object into a MongoDB, using the following bit of code:

public class myClass
{
    public string Heading { get; set; }
    public string Body { get; set; } 
}

static void Main(string[] args)
{
    var mongo = MongoServer.Create();
    var db = mongo.GetDatabase("myDb");
    var col = db.GetCollection<BsonDocument>("myCollection");
    var myinstance = new myClass();
    col.Insert(myinstance);

    var query = Query.And(Query.EQ("_id", new ObjectId("4df06c23f0e7e51f087611f7)));
    var res = col.Find(query);
    foreach (var doc in res)
    {
        var obj = BsonSerializer.Deserialize<myClass>(doc);
    }
}

However I get the following exception 'Unexpected element: _id' when trying to Deserialize the document.

So do I need to Deserialize in another way?? What is the preferred way of doing this?

TIA

Søren

like image 720
smolesen Avatar asked Jun 09 '11 07:06

smolesen


2 Answers

You are searching for a given document using an ObjectId but when you save an instance of MyClass you aren't providing an Id property so the driver will create one for you (you can make any property the id by adding the [BsonId] attribute to it), when you retrieve that document you don't have an Id so you get the deserialization error.

You can add the BsonIgnorExtraElements attribute to the class as Chris said, but you should really add an Id property of type ObjectId to your class, you obviously need the Id (as you are using it in your query). As the _id property is reserved for the primary key, you are only ever going to retrieve a single document so you would be better off writing your query like this:

col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));

The fact that you are deserializing to an instance of MyClass once you retrieve the document lends itself to strongly typing the collection, so where you create an instance of the collection you can do this

var col = db.GetCollection<MyClass>("myCollection");

so that when you retrieve the document using the FindOneById method the driver will take care of the deserialization for you putting it all together (provided you add the Id property to the class) you could write

var col = db.GetCollection<MyClass>("myCollection");
MyClass myClass = col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));

One final thing to note, as the _id property is created for you on save by the driver, if you were to leave it off your MyClass instance, every time you saved that document you would get a new Id and hence a new document, so if you saved it n times you would have n documents, which probably isn't what you want.

like image 105
nickbw Avatar answered Oct 12 '22 02:10

nickbw


A slight variation of Projapati's answer. First Mongo will deserialize the id value happily to a property named Id which is more chsarp-ish. But you don't necessarily need to do this if you are just retrieving data.

You can add [BsonIgnoreExtraElements] to your class and it should work. This will allow you to return a subset of the data, great for queries and view-models.

like image 44
Chris Nicola Avatar answered Oct 12 '22 01:10

Chris Nicola