2
votes

I have a class A and a class B and C

public class A  
{ 
    public string Id {get;set;}
    public List<B> Children {get;set;}
}
public class B
{
    public string Id {get;set;}
    public string Foo {get;set;}
    public double Bar {get;set;}
}
public class C 
{
    public string Id {get;set;}
    //This property will hold a serialized version of Class A.
    //The scenario requirement is that it can hold any arbitrary BsonDocument of different types
    public BsonDocument Properties {get;set;} 
}

var instanceOfClassC = collection.Find(...).First();

var data = BsonSerializer.Deserialize<A>(instanceOfClassC.Properties);

This last line causes the exception bellow. If I add ignore BsonElement to the Id property of class B it works fine. But I need that Id property!

Exception:

An unhandled exception of type 'System.FormatException' occurred in MongoDB.Bson.dll

Additional information: An error occurred while deserializing the Children property of class NameSpace.A: Element 'Id' does not match any field or property of class NameSpace.B.

An unhandled exception of type 'System.FormatException' occurred in MongoDB.Bson.dll

The problem seems to be that the property B.Id is actually stored as "Id" in MongoDb since it's "serialized" to a BsonDocument before storage. The same pattern otherwise always works perfect, but than MongoDb will transform Id => _id at write time.

Requirement: class C.Properties contains arbitrary kinds of other valid class types and cannot be changed to type A in class declaration. It works smooth - except that nested Id property!

Update: Found a brutal hack solution: Rename all "Id" properties in the BsonDocument to "_id" before shipping of to MongoDb. Then deserialization works as expected. I do this with string replace of a json json.Replace("\"Id\"", "\"_id\"")

Anyone has a better solution?

1

1 Answers

5
votes

A cleaner solution would be:

public class A  
{ 
    public string Id {get;set;}
    public List<B> Children {get;set;}
}
[BsonNoId] // this solves the problem
public class B
{
    public string Id {get;set;}
    public string Foo {get;set;}
    public double Bar {get;set;}
}

You can also do this without an attribute, using the BsonClassMap:

BsonClassMap.RegisterClassMap<B>(cm =>
{
    cm.SetIdMember(null);
});

Which is usful if the types you are mapping are from a different library