Mongo C# Driver and ObjectID JSON String Format in .NET Core
Use BsonDocument when saving to MongoDB
After trying a number of different configurations, the only way I was able to correctly save truly dynamic documents using the connector was to parse objects as BsonDocument
s.
public ActionResult Post([FromBody]JObject resource)
{
var document = BsonDocument.Parse(resource.ToString(Formatting.None));
DbContext.Resources.InsertOne(document);
}
Register BsonDocument
serializers with JSON.Net
The problem with the above approach initially was that when calling ToJson()
the ISODate
and ObjectId
objects would be serialized into objects, which was undesirable. At the time of writing, there doesn't seem to be any extensibility points for overriding this behavior. The logic is baked into the MongoDB.Bson.IO.JsonWriter
class, and you cannot register BsonSerializer
s for BsonValue
types:
MongoDB.Bson.BsonSerializationException: A serializer cannot be registered for type BsonObjectId because it is a subclass of BsonValue.
At the time of writing, the only solution I've found is to explicitly custom JSON.Net converters. MongoDB C# Lead Robert Stam has created an unpublished library for this which community member Nathan Robinson has ported to .net-core.. I've created a fork that properly serializes the ObjectId and ISODate fields.
I've created a NuGet package from their work. To use it, include the following reference in your .csproj
file:
<PackageReference Include="MongoDB.Integrations.JsonDotNet" Version="1.0.0" />
Then, explicitly register the converters:
Startup.cs
using MongoDB.Integrations.JsonDotNet.Converters;
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
// Adds automatic json parsing to BsonDocuments.
options.SerializerSettings.Converters.Add(new BsonArrayConverter());
options.SerializerSettings.Converters.Add(new BsonMinKeyConverter());
options.SerializerSettings.Converters.Add(new BsonBinaryDataConverter());
options.SerializerSettings.Converters.Add(new BsonNullConverter());
options.SerializerSettings.Converters.Add(new BsonBooleanConverter());
options.SerializerSettings.Converters.Add(new BsonObjectIdConverter());
options.SerializerSettings.Converters.Add(new BsonDateTimeConverter());
options.SerializerSettings.Converters.Add(new BsonRegularExpressionConverter());
options.SerializerSettings.Converters.Add(new BsonDocumentConverter());
options.SerializerSettings.Converters.Add(new BsonStringConverter());
options.SerializerSettings.Converters.Add(new BsonDoubleConverter());
options.SerializerSettings.Converters.Add(new BsonSymbolConverter());
options.SerializerSettings.Converters.Add(new BsonInt32Converter());
options.SerializerSettings.Converters.Add(new BsonTimestampConverter());
options.SerializerSettings.Converters.Add(new BsonInt64Converter());
options.SerializerSettings.Converters.Add(new BsonUndefinedConverter());
options.SerializerSettings.Converters.Add(new BsonJavaScriptConverter());
options.SerializerSettings.Converters.Add(new BsonValueConverter());
options.SerializerSettings.Converters.Add(new BsonJavaScriptWithScopeConverter());
options.SerializerSettings.Converters.Add(new BsonMaxKeyConverter());
options.SerializerSettings.Converters.Add(new ObjectIdConverter());
});
}
}
Now, you can serialize using the default serializer:
return Created($"resource/{document["_id"].ToString()}", document);
You can make your last attempt work by registering custom ObjectIdConverter
with NewtonSoft.
await resources = _database.GetCollection<dynamic>("resources")
.Find(Builders<dynamic>.Filter.Empty)
.ToListAsync();
return Ok(Newtonsoft.Json.JsonConvert.SerializeObject(resources, new ObjectIdConverter()));
Converter:
class ObjectIdConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(ObjectId).IsAssignableFrom(objectType);
}
}
Note: The above converter converts from ObjectId
to String after the BSONSerailzers
have converted bson value to ObjectId
.
You'll still need to use parse to convert string id to ObjectIds for queries and register the ObjectIdConverter globally.
Reference: https://stackoverflow.com/a/16693462/2683814