MongoDB插入时抛出BsonSerializationException异常的解决咨询
这个问题本质是MongoDB的BSON文档字段名有严格规则:不能包含/、.、$这些特殊字符,而你的Metadata字典用了带/和:的URL作为键,默认序列化会把字典键直接转成BSON字段名,所以触发了BsonSerializationException。下面给你几个可行的解决方案:
方案1:使用特性指定字典的序列化方式(最简单)
直接给Metadata属性添加BsonDictionaryOptions特性,指定字典序列化为数组形式的文档,而不是嵌入式的键值对。这样MongoDB会把字典存储为包含k(键)和v(值)的对象数组,完全避开非法字段名的问题。
修改你的模型:
using MongoDB.Bson.Serialization.Attributes; public class UserInfo{ public string Id{get;} [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] public Dictionary<string, string> Metadata{ get; } }
这样插入数据时,Metadata会被序列化成类似这样的BSON结构:
"Metadata": [ { "k": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "v": "your-value" }, { "k": "other-key", "v": "other-value" } ]
读取数据时,MongoDB驱动会自动把这个数组反序列回Dictionary<string, string>,完全不影响你的业务逻辑。
方案2:自定义字典键的序列化器(保持嵌入式结构)
如果你希望保持字典的嵌入式结构(即键直接作为BSON字段名),可以自定义一个序列化器,把非法的键字符替换成合法字符,反序列化时再还原。
示例代码:
using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; public class SafeDictionarySerializer : DictionarySerializerBase<Dictionary<string, string>, string, string> { protected override Dictionary<string, string> CreateInstance() => new(); protected override string DeserializeKey(BsonDeserializationContext context) { // 反序列化时把替换的字符还原回原URL var key = base.DeserializeKey(context); return key.Replace("_slash_", "/").Replace("_colon_", ":"); } protected override void SerializeKey(BsonSerializationContext context, string key) { // 序列化时把URL中的非法字符替换成合法的 var safeKey = key.Replace("/", "_slash_").Replace(":", "_colon_"); base.SerializeKey(context, safeKey); } }
然后在模型上指定使用这个序列化器:
[BsonSerializer(typeof(SafeDictionarySerializer))] public Dictionary<string, string> Metadata{ get; }
这样存储时,URL键会被转换成http__slash__schemas.xmlsoap.org_slash_ws_slash_2005_slash_05_slash_identity_slash_claims_slash_name这种合法的字段名,读取时自动还原成原URL。
方案3:修改模型结构为键值对列表
如果可以接受修改模型的存储结构,把Dictionary<string, string>改成List<KeyValuePair<string, string>>,这样存储时每个键值对都是独立的对象,不存在非法字段名的问题:
public class UserInfo{ public string Id{get;} public List<KeyValuePair<string, string>> Metadata{ get; } }
这种方式的优点是完全不需要处理序列化逻辑,缺点是你在业务代码中需要把字典转换成列表,或者直接用列表来操作数据。
内容的提问来源于stack exchange,提问作者Yahya Hussein

