You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# MongoDB反序列化JsonObject报错:无无参构造函数定义

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:20:41