C# MongoDB反序列化JsonObject报错:无无参构造函数定义
问题背景
我在C#项目中定义了如下Item类:
public class Item { [BsonId] public ObjectId Id { get; set; } public string AppId { get; set; } public JsonObject Fields { get; set; } }
通过POST请求的如下Body在MongoDB中创建了若干条数据:
{ "AppId":"(an already existing app id)", "Fields": { "NombreCliente": "Pepillo", "Email": "pepiko@email.com", "Telefono": "pp56656784", "Localidad": "Pepeland", "Facturas": ["848435498","0564864984"] } }
数据已成功创建,但查询时出现报错:
An error occurred while deserializing the Fields property of class divitiae_api.Models.Item: Cannot dynamically create an instance of type 'System.Text.Json.Nodes.JsonObject'. Reason: No parameterless constructor defined.
我尝试给Item类添加无参构造函数,但问题仍未解决。
更新说明
由于直接使用Item类插入会出现Field属性循环引用异常,我采用以下方法插入数据:
public async Task InsertItem(JsonObject item) { var options = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve }; var jsonString = System.Text.Json.JsonSerializer.Serialize(item, options); var document = BsonSerializer.Deserialize<BsonDocument>(jsonString); var collection2 = _repository.db.GetCollection<BsonDocument>("Items"); await collection2.InsertOneAsync(document); }
查询数据使用的方法如下:
public async Task<List<Item>> GetAllAppItems(string appId) { var filter = Builders<Item> .Filter .Eq(s => s.AppId, appId); return await Collection.FindAsync(new BsonDocument()).Result.ToListAsync(); }
请问该如何解决这个问题?
解决方案
原因分析
MongoDB官方驱动默认不支持直接反序列化System.Text.Json.Nodes.JsonObject类型——该类型本身没有公开的无参构造函数,即使给Item类加了无参构造也没用。另外你插入数据时先转JSON再转BsonDocument的流程,导致存储的结构和驱动期望的JsonObject结构不匹配,进一步触发反序列化失败。
具体解决办法
方法1:自定义Bson序列化器处理JsonObject
为JsonObject编写自定义序列化器,让MongoDB驱动能正确处理该类型:
public class JsonObjectSerializer : IBsonSerializer<JsonObject> { public Type ValueType => typeof(JsonObject); public JsonObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(context.Reader); return JsonSerializer.Deserialize<JsonObject>(bsonDoc.ToJson()); } public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JsonObject value) { var jsonStr = JsonSerializer.Serialize(value); var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(jsonStr); BsonSerializer.Serialize(context.Writer, bsonDoc); } // 实现非泛型接口方法 object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { return Deserialize(context, args); } void IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { Serialize(context, args, (JsonObject)value); } }
然后在Item类的Fields属性上标记使用该序列化器:
[BsonSerializer(typeof(JsonObjectSerializer))] public JsonObject Fields { get; set; }
方法2:替换JsonObject为BsonDocument
既然插入时用的是BsonDocument,直接把Fields类型改成BsonDocument更简单,避免序列化冲突:
修改Item类:
public class Item { [BsonId] public ObjectId Id { get; set; } public string AppId { get; set; } public BsonDocument Fields { get; set; } }
插入方法可以简化:
public async Task InsertItem(Item item) { await Collection.InsertOneAsync(item); }
如果业务需要JsonObject,可以在查询后转换:
var items = await Collection.Find(filter).ToListAsync(); var result = items.Select(i => new { i.Id, i.AppId, Fields = JsonSerializer.Deserialize<JsonObject>(i.Fields.ToJson()) }).ToList();
方法3:用Dictionary<string, object>替代JsonObject
如果不需要JsonObject的特定功能,换成Dictionary<string, object>——MongoDB驱动原生支持该类型的序列化,还能解决之前的循环引用问题:
修改Item类:
public class Item { [BsonId] public ObjectId Id { get; set; } public string AppId { get; set; } public Dictionary<string, object> Fields { get; set; } }
这种方式无需额外配置,插入和查询都能直接使用Item类。
额外优化:修复查询方法的bug
你当前的查询方法定义了filter但实际用的是new BsonDocument()(查所有数据),应该改成使用定义好的过滤器,同时去掉.Result避免阻塞:
public async Task<List<Item>> GetAllAppItems(string appId) { var filter = Builders<Item> .Filter .Eq(s => s.AppId, appId); return await Collection.Find(filter).ToListAsync(); }
内容的提问来源于stack exchange,提问作者Nono-Man

