[CSHARP-3108] Deserialization throws No matching creator found exception Created: 20/May/20  Updated: 27/Oct/23  Resolved: 17/Jun/20

Status: Closed
Project: C# Driver
Component/s: BSON
Affects Version/s: 2.10.3, 2.10.4
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: vargese antony Assignee: Robert Stam
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Windows 10


Issue Links:
Duplicate
is duplicated by CSHARP-3175 Regression in v2.10.2 - No matching c... Closed
is duplicated by CSHARP-3186 Regression upgrading driver from 2.7 ... Closed
Related
related to CSHARP-2889 BsonClassMap.LookupClassMap supports ... Closed
related to CSHARP-3845 Deserialize Select of anonymous types... Closed

 Description   

When reading the data using C# driver 2.10.4, below exception is thrown if document has not all the attributes,

"An error occurred while deserializing the property <property name> of class <clas name>: No matching creator found."

When using the driver version 2.10.1, we do not see the issue

Mongo DB document:

{
   "_id":"23456",
   "type":"address",
   "attributes":{
      "name":"San",
      "address":[
         {
            "name":"address1",
            "street":"1430 Del santra, SA",
            "city":"NY"
         },
         {
            "name":"address2",
            "city":"NY"
         }
      ]
   }
}

Sample program to reproduce:

MongoUrl conString = new MongoUrl("mongodb://localhost:27017/");
 
IMongoClient dbConnect = new MongoClient(conString);
IMongoDatabase _dbMongo = dbConnect.GetDatabase("ApplicationDB");
 
ConventionPack _conventions = new ConventionPack();
_conventions.Add(new LowerCaseElementNameConvention());
ConventionRegistry.Register("lowercaseconvention", _conventions, t => true);
_conventions.Add(new IgnoreExtraElementsConvention(true));
ConventionRegistry.Register("IgnoreExtraElements", _conventions, t => true);
 
try
{
	var result = Task.Run( () => _dbMongo.GetCollection<AddressDetails>("simplecollection").Find(x => x.Type == "address" && x.Attributes.Name == "San").FirstOrDefaultAsync());
	result.Wait();
}
catch (MongoException ex)
{
	Console.WriteLine($"{ex.Message}");
}
 
public class AddressDetails
{
	public string Id { get; set; }
	public Attributes Attributes { get; private set; }
	public string Type { get; private set; }
 
public AddressDetails(string id, Attributes attributes, string type)
{
	Id = id;
	Attributes = attributes;
	Type = type;
}
 
public class Attributes
{
	public string Name { get; private set; }
	public List<Address> Address { get; private set; }
	public Attributes(string name, List<Address> address)
	{
		Name = name;
		Address = address;
	}
}
 
public class Address
{
	public string Name { get; private set; }
	public string Street { get; private set; }
	public string City { get; private set; }
	public Address(string name, string street, string city)
	{
		Name = name;
		Street = street;
		City = city;
	}
}



 Comments   
Comment by Nicola Cassolato [ 03/Sep/21 ]

Hi @Robert,

we're upgrading a large project to the latest version of the MongoDb c# driver and the issues presented in this ticket is impacting us.

We cannot use decorators in our classes (they are in a project without referenced to the mongdb driver)

Is there a way to specifify BsonDefaultValue(null) for all properties in all classes without the decorator? 

Thanks,
Nicola

Comment by Robert Stam [ 17/Jun/20 ]

Thank you for reporting this. You are right that there is a change in behavior for classes like Address starting with version 2.10.2 of the C# driver.

The change is because we now recognize this class as an immutable class and as such will now instantiate instances of this class during deserialization by calling the constructor (we used to use reflection to create an uninitialized object and to force values into the uninitialized instance by also using reflection to call the private setters, which is not the best approach because it bypasses any validation or additional initialization the constructor might wish to do).

The exception is being thrown because the document does not have a value for the Street property and therefore we don't have all the needed values to pass to the constructor.

You can tell the driver to use a default value for missing fields by using the  [BsonDefaultValue] annotation, as in:

public class Address
{
    public string Name { get; private set; }
    [BsonDefaultValue(null)]
    public string Street { get; private set; }
    public string City { get; private set; }
    public Address(string name, string street, string city)
    {
        Name = name;
        Street = street;
        City = city;
    }
}

I'm sorry this change caused a breaking change for you, but I think this is the proper behavior and I hope the existence of a workaround that lets you explicitly specify which fields should be considered optional (by providing default values for them) will work for you.

Comment by vargese antony [ 20/May/20 ]

One update:

When using the driver version 2.10.0, we do not see the issue

Generated at Wed Feb 07 21:44:23 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.