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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 23:22:52