修复MongoDB C#驱动BsonString/BsonArray转BsonBoolean类型错误
问题根因
这个异常和MongoDB查询逻辑无关,是Swagger/ASP.NET Core模型绑定生成器在处理带[BsonExtraElements]标记的BsonDocument类型时出现序列化配置冲突,触发点为Swagger运行时生成接口Schema的阶段,因此会出现还未执行接口业务逻辑就崩溃的现象。
具体触发逻辑:
- MongoDB C#驱动默认会给
BsonDocument类型配置专属动态序列化规则,会把未明确声明类型的字段默认按BsonBoolean类型做初始映射校验 - ASP.NET Core默认的System.Text.Json序列化器没有内置Bson类型适配规则,Swashbuckle(Swagger组件)在扫描POST接口入参模型生成Schema时,会递归解析
BsonDocument下的所有属性类型,遇到BsonValue的隐式类型转换逻辑时就会抛出类型不匹配异常 - 你贴出的代码中仅包含GET接口,实际触发异常的POST接口应该是直接接收
Incident类型作为入参,这是Schema生成时扫描到ExtraElements属性触发报错的直接原因。
排查步骤
- 先确认异常触发阶段:注释掉POST接口中所有和MongoDB操作相关的代码,仅保留空返回逻辑,重新启动项目访问Swagger页面,会发现异常依旧存在,可证明问题和数据库操作无关,出在接口模型扫描阶段。
- 查看异常堆栈:可以看到异常抛出位置为
Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator或者System.Text.Json.JsonSerializer的类型解析逻辑,而非MongoDB驱动代码,可进一步确认是序列化配置冲突。
修复方案
按实际场景选择以下任意一种方案即可:
方案1:给ExtraElements属性添加JsonIgnore特性(最简便)
直接让System.Text.Json和Swagger忽略这个动态属性,避免Schema生成时递归解析BsonDocument类型。MongoDB驱动本身可独立识别[BsonExtraElements]特性,不会影响数据库字段的读写映射。
修改后的Incident模型代码:
using System.Text.Json.Serialization; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; public class Incident { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string? Name { get; set; } [BsonExtraElements] [JsonIgnore] // 新增该行 public BsonDocument? ExtraElements { get; set; } }
注意:如果POST接口需要接收前端传入的动态额外字段,该方案下需要拆分接口入参和数据库实体模型,不要直接用Incident作为POST入参。
方案2:配置Bson类型的System.Text.Json序列化规则
如果需要接口直接序列化/反序列化包含ExtraElements的实体,可添加Bson类型的Json转换器解决类型解析冲突:
- 自定义BsonValue通用Json转换器:
public class BsonValueJsonConverter : JsonConverter<BsonValue> { public override BsonValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch { JsonTokenType.True => BsonBoolean.True, JsonTokenType.False => BsonBoolean.False, JsonTokenType.Number when reader.TryGetInt64(out var l) => new BsonInt64(l), JsonTokenType.Number => new BsonDouble(reader.GetDouble()), JsonTokenType.String => new BsonString(reader.GetString()), JsonTokenType.StartArray => BsonArray.Parse(JsonDocument.ParseValue(ref reader).RootElement.GetRawText()), JsonTokenType.StartObject => BsonDocument.Parse(JsonDocument.ParseValue(ref reader).RootElement.GetRawText()), JsonTokenType.Null => BsonNull.Value, _ => throw new JsonException($"不支持的BsonValue类型: {reader.TokenType}") }; } public override void Write(Utf8JsonWriter writer, BsonValue value, JsonSerializerOptions options) { writer.WriteRawValue(value.ToJson()); } }
- 在Program.cs中全局注册转换器:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new BsonValueJsonConverter()); });
- 如果Swagger仍存在Schema生成异常,额外给Swagger生成器添加BsonDocument类型的Schema映射:
builder.Services.AddSwaggerGen(c => { c.MapType<BsonDocument>(() => new OpenApiSchema { Type = "object", AdditionalPropertiesAllowed = true }); c.MapType<BsonArray>(() => new OpenApiSchema { Type = "array" }); c.MapType<BsonValue>(() => new OpenApiSchema { }); });
方案3:拆分入参模型和数据库实体模型(工程化推荐)
不要直接把MongoDB实体类作为接口入参/出参,单独定义Incident的请求/响应DTO,动态字段用Dictionary<string, object>类型接收,在业务逻辑层做DTO和Mongo实体的转换,从根源上避免MongoDB专属类型和Web层序列化逻辑耦合。
示例代码:
// 接口入参DTO public class IncidentCreateDto { public string? Name { get; set; } public Dictionary<string, object>? ExtraFields { get; set; } } // 业务层转换逻辑 public Incident DtoToEntity(IncidentCreateDto dto) { return new Incident { Name = dto.Name, ExtraElements = dto.ExtraFields == null ? null : BsonDocument.Create(dto.ExtraFields) }; }
修复验证
修复完成后先访问Swagger页面确认页面可正常加载,再测试接口逻辑:
- 写入包含字符串、布尔值、数组类型的动态字段到MongoDB,不会出现类型转换异常
- 查询返回的结果中,额外字段可正常映射到ExtraElements属性中
内容的提问来源于stack exchange,提问作者Robert Rachita
相关产品推荐
相关产品推荐

