如何在C# MongoDriver中阻止ObjectId转为$oid字符串格式
我在.NET程序中向MongoDB插入包含ObjectId列表的BsonDocument,插入前期望的格式是:
[ { "_id" : ObjectId("<id1>") }, { "_id" : ObjectId("<id2>") }, { "_id" : ObjectId("<Id3>") } ]
但插入后实际得到的格式变成了:
[ { "_id" : { "$oid" : "<id1>" } }, { "_id" : { "$oid" : "<id2>" } }, { "_id" : { "$oid" : "<id3>" } } ]
初始化Id列表的代码如下:
public BsonArray GetSimilarDocumentsIds() { // similarIds是string类型的Id列表 var keys = similarIds.Keys.ConvertAll(x => new BsonDocument("_id", new ObjectId(x))); var result = new BsonArray(); foreach (var id in keys) { result.Add(id.ToBsonDocument()); } return result; } //-- 程序后续某处 mainDocument.Add("SimilarIds", GetSimilarDocumentsIds()) await db.InsertOneAsync(mainDocument);
我尝试过设置BsonRepresentation、包装对象以及自定义序列化器,但都没有效果。我知道这是MongoDB扩展JSON的特性,但必须修复这个问题,因为当前格式破坏了约定。
首先要明确:MongoDB内部存储的ObjectId是12字节的二进制数据,你看到的{"$oid": "<id>"}是**扩展JSON(Extended JSON)**的标准表示,用于在原生JSON中兼容MongoDB的特殊类型;而ObjectId("<id>")是Mongo Shell的语法格式。以下是针对性的修复方案:
1. 全局/字段级自定义ObjectId序列化器
如果需要在.NET程序序列化/反序列化时得到ObjectId("<id>")格式,可以自定义序列化器替换默认逻辑:
public class CustomObjectIdSerializer : IBsonSerializer<ObjectId> { public Type ValueType => typeof(ObjectId); public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, ObjectId value) { var writer = context.Writer; // 写入Mongo Shell风格的ObjectId字面量 writer.WriteJavaScript($"ObjectId(\"{value.ToString()}\")"); } public ObjectId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var reader = context.Reader; // 兼容两种格式的反序列化 if (reader.CurrentBsonType == BsonType.JavaScript) { var jsString = reader.ReadJavaScript(); var oidMatch = System.Text.RegularExpressions.Regex.Match(jsString, @"ObjectId\(""([a-f0-9]{24})""\)"); if (oidMatch.Success) { return ObjectId.Parse(oidMatch.Groups[1].Value); } } else if (reader.CurrentBsonType == BsonType.Document) { reader.ReadStartDocument(); reader.ReadName("$oid"); var oidStr = reader.ReadString(); reader.ReadEndDocument(); return ObjectId.Parse(oidStr); } throw new InvalidOperationException("无法反序列化ObjectId"); } }
注册序列化器
- 全局注册(影响所有ObjectId类型):
BsonSerializer.RegisterSerializer(new CustomObjectIdSerializer()); - 字段级注册(仅影响特定字段):
如果你的文档是强类型,可以给目标字段标记特性:public class YourDocument { [BsonSerializer(typeof(CustomObjectIdSerializer))] public List<BsonDocument> SimilarIds { get; set; } }
2. 手动构造字符串格式(仅特殊场景使用)
如果不需要将值作为MongoDB的ObjectId类型存储,只是要保存ObjectId("<id>")格式的字符串,可以直接构造JavaScript字面量:
public BsonArray GetSimilarDocumentsIds() { var result = new BsonArray(); foreach (var idStr in similarIds.Keys) { result.Add(new BsonDocument("_id", new BsonJavaScript($"ObjectId(\"{idStr}\")"))); } return result; }
⚠️ 注意:这种方式会把_id存储为JS字符串,而非MongoDB原生ObjectId类型,会影响后续基于ObjectId的查询匹配,仅在不需要类型匹配的场景下使用。
3. 理解存储与序列化的差异
如果是在Mongo Shell中查询看到$oid格式,这是Shell的默认展示逻辑,属于正常现象——Shell会用扩展JSON来解析并展示MongoDB的二进制类型数据,此时无需修改存储逻辑,若需要Shell中显示ObjectId("<id>")格式,直接在Shell中查询即可(Shell会自动解析$oid为ObjectId字面量)。
内容的提问来源于stack exchange,提问作者NoobMaster69

