.NET API中System.Text.Json.JsonElement MongoDB序列化报错求助
解决.NET API中MongoDB序列化System.Text.Json.JsonElement的异常
在.NET API中构建包含columns和content的动态模型时,将数据存入MongoDB触发MongoDB.Bson.BsonSerializationException,错误提示:
System.Text.Json.JsonElement类型未在此ObjectSerializer实例中配置为可序列化类型
错误堆栈信息
MongoDB.Bson.BsonSerializationException: An error occurred while serializing the content property of class pm_management.API.Domain.Models.ProjectIdentification: Type System.Text.Json.JsonElement is not configured as a type that is allowed to be serialized for this instance of ObjectSerializer. ---> MongoDB.Bson.BsonSerializationException: Type System.Text.Json.JsonElement is not configured as a type that is allowed to be serialized for this instance of ObjectSerializer. at MongoDB.Bson.Serialization.Serializers.ObjectSerializer.SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, Object value, Type actualType) at MongoDB.Bson.Serialization.Serializers.ObjectSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value) at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Serialize[TValue](IBsonSerializer`1 serializer, BsonSerializationContext context, TValue value) at MongoDB.Bson.Serialization.Serializers.EnumerableSerializerBase`2.Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value) at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Serialize(IBsonSerializer serializer, BsonSerializationContext context, Object value) at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.SerializeMember(BsonSerializationContext context, Object obj, BsonMemberMap memberMap)
相关代码
模型类及扩展方法
using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System.Globalization; using System.Text.Json; namespace pm_management.API.Domain.Models { [BsonIgnoreExtraElements] public class ProjectIdentification { [BsonId] [BsonRepresentation(BsonType.String)] public string? Id { get; set; } public Object[] content { get; set; } public Object[] columns { get; set; } public Theme theme { get; set; } public void SetContentAndColumns(string contentJson, string columnsJson) { content = JsonSerializer.Deserialize<Object[]>(contentJson); columns = JsonSerializer.Deserialize<Object[]>(columnsJson); } } public static class BsonExtensions { public static BsonDocument ToBsonDocument(this JsonElement e, bool writeRootArrayAsDocument = false, bool tryParseDateTimes = false) => e.ValueKind switch { JsonValueKind.Object => new(e.EnumerateObject().Select(p => new BsonElement(p.Name, p.Value.ToBsonValue(tryParseDateTimes)))), JsonValueKind.Array when writeRootArrayAsDocument => new(e.EnumerateArray().Select((v, i) => new BsonElement(i.ToString(NumberFormatInfo.InvariantInfo), v.ToBsonValue(tryParseDateTimes)))), _ => throw new NotSupportedException($"ToBsonDocument: {e}"), }; public static BsonValue ToBsonValue(this JsonElement e, bool tryParseDateTimes = false) => e.ValueKind switch { JsonValueKind.String when tryParseDateTimes && e.TryGetDateTime(out var v) => BsonValue.Create(v), JsonValueKind.String => BsonValue.Create(e.GetString()), JsonValueKind.Number when e.TryGetInt32(out var v) => BsonValue.Create(v), JsonValueKind.Number when e.TryGetInt64(out var v) => BsonValue.Create(v), JsonValueKind.Number when e.TryGetDouble(out var v) => BsonValue.Create(v), JsonValueKind.Null => BsonValue.Create(null), JsonValueKind.True => BsonValue.Create(true), JsonValueKind.False => BsonValue.Create(false), JsonValueKind.Array => new BsonArray(e.EnumerateArray().Select(v => v.ToBsonValue(tryParseDateTimes))), JsonValueKind.Object => e.ToBsonDocument(false, tryParseDateTimes), _ => throw new NotSupportedException($"ToBsonValue: {e}"), }; } }
仓储代码
private readonly IMongoCollection<ProjectIdentification> _collection; public ProjectIdentificationRepository(IOptions<MongoDbSettings> mongoDbSettings) { var mongoClient = new MongoClient( mongoDbSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( mongoDbSettings.Value.DatabaseName); _collection = mongoDatabase.GetCollection<ProjectIdentification>("Project"); } public async Task AddProject(ProjectIdentification project) { string serializedJson = JsonSerializer.Serialize(project, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); ProjectIdentification deserializedProject = JsonSerializer.Deserialize<ProjectIdentification>(serializedJson); await _collection.InsertOneAsync(deserializedProject); } public async Task<ProjectIdentification> GetProjectById(string id) { var filter = Builders<ProjectIdentification>.Filter.Eq("_id", ObjectId.Parse(id)); var result = await _collection.Find(filter).FirstOrDefaultAsync(); return result ?? null; }
示例数据
{ "_id": { "$oid": "65edc0c6482da8bd8368fbbf" }, "Id": "65edc0c6482da8bd8368fbbe", "content": [ { "id": "z5LllRt-NoiGkoisa_2tN", "type": "image", "data": { "style": "circle", "image": "/images/profile.jpg" } }, { "id": "QfAFa0golVKIImL764OCO", "type": "text", "data": { "headline": "Horst Karacho", "description": "My name is Horst :)", "rank":"test", "anotherfield":"tesst" } }, { "id": "j3ofcLmYExoG0AS_yoRq0", "type": "text", "data": { "headline": "Lorem Ipsum", "description": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna...", "details": { "author": "Unknown", "publishedDate": "2023-01-15", "wordCount": 245 } } } ], "columns": [ { "id": "mOceUBR-oB5xEzfstycFh", "content": [ "qkx4WlS5XkUZcfTpy9rN0", "qkx4WlS5XkUZcfTpy9rN1", "qkx4WlS5XkUZcfTpy9rN11", "qkx4WlS5XkUZcfTpy9rN12", "qkx4WlS5XkUZcfTpy9rN3", "qkx4WlS5XkUZcfTpy9rN4", "qkx4WlS5XkUZcfTpy9rN5", "qkx4WlS5XkUZcfTpy9rN6", "qkx4WlS5XkUZcfTpy9rN7", "qkx4WlS5XkUZcfTpy9rN8", "qkx4WlS5XkUZcfTpy9rN9" ] } ], "theme": { "HighlightColor": "#3f51b5", "HeadlineColor": "#95A5A6", "HeadlineFontType": "Roboto", "HeadlineFontSize": "1.8em", "HeadlineFontWeight": "600", "HeadlineBorderType": "dashed", "TextColor": "#333", "TextFontType": "Source Sans Pro", "TextFontSize": "1em", "TextFontWeight": "200" } }
问题根源
使用JsonSerializer.Deserialize<Object[]>反序列化时,实际返回的是JsonElement类型的数组,而MongoDB的默认ObjectSerializer不支持直接序列化JsonElement类型。此外,AddProject方法中多余的序列化-反序列化操作会加剧这个问题,导致对象类型被转换为JsonElement。
解决方案
1. 自定义JsonElement的Bson序列化器
创建序列化器将JsonElement转换为MongoDB支持的BsonValue:
using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using System.Text.Json; public class JsonElementBsonSerializer : SerializerBase<JsonElement> { public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JsonElement value) { var bsonValue = value.ToBsonValue(); context.Writer.WriteBsonValue(bsonValue); } public override JsonElement Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonValue = context.Reader.ReadBsonValue(); using var tempDoc = new BsonDocument("val", bsonValue); var json = tempDoc.ToJson(); using var doc = JsonDocument.Parse(json); return doc.RootElement.GetProperty("val"); } }
在应用启动时注册该序列化器:
BsonSerializer.RegisterSerializer(new JsonElementBsonSerializer());
2. 修改模型属性类型
将模型中的Object[]改为JsonElement[],明确类型:
public JsonElement[] content { get; set; } public JsonElement[] columns { get; set; }
同时更新SetContentAndColumns方法的反序列化逻辑:
public void SetContentAndColumns(string contentJson, string columnsJson) { content = JsonSerializer.Deserialize<JsonElement[]>(contentJson); columns = JsonSerializer.Deserialize<JsonElement[]>(columnsJson); }
3. 移除多余的序列化循环
删除AddProject中不必要的序列化-反序列化步骤,直接使用传入的对象:
public async Task AddProject(ProjectIdentification project) { await _collection.InsertOneAsync(project); }
完成以上步骤后,MongoDB就能正确序列化JsonElement类型的属性,解决异常问题。
内容的提问来源于stack exchange,提问作者PASHA
相关产品推荐
相关产品推荐

