Going from .NET objects to JSON to BSON, while at the same time avoiding _t/_v encoding, will result in loss of type information for dates and other BSON-specific types.
The solution that I found for avoiding _t/_v while preserving the BSON types was to register a custom value serializer for the driver’s built-in DictionaryInterfaceImplementerSerializer
(and optionally the EnumerableInterfaceImplementerSerializer
, too). I have tested the solution for “Extended JSON” generation, i.e. JSON with special syntax for the BSON-specific types, but the principle should be the same for direct BSON output.
To do this, first copy the standard ObjectSerializer
into your C# project (and rename it to avoid ambiguity): https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs
In addition to the class renaming, the private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType)
method also needs to be edited. (In my case, I also wanted to avoid _t/_v encoding for IEnumerable<object>
, which is reflected below.)
private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType)
{
var serializer = BsonSerializer.LookupSerializer(actualType);
var polymorphicSerializer = serializer as IBsonPolymorphicSerializer;
// Added line
var assignableToDictionaryOrEnumerable = typeof(IDictionary<string, object>).IsAssignableFrom(actualType) || typeof(IEnumerable<object>).IsAssignableFrom(actualType);
if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer)
{
serializer.Serialize(context, args, value);
}
else
{
// Edited line
if (assignableToDictionaryOrEnumerable || (context.IsDynamicType != null && context.IsDynamicType(value.GetType())))
{
// We want this code to be executed for types that should be serialized without _t and _v fields
args.NominalType = actualType;
serializer.Serialize(context, args, value);
}
else
{
var bsonWriter = context.Writer;
var discriminator = _discriminatorConvention.GetDiscriminator(typeof(object), actualType);
bsonWriter.WriteStartDocument();
bsonWriter.WriteName(_discriminatorConvention.ElementName);
BsonValueSerializer.Instance.Serialize(context, discriminator);
bsonWriter.WriteName("_v");
serializer.Serialize(context, value);
bsonWriter.WriteEndDocument();
}
}
}
Assuming that the name of the copied and edited class is DynamicValueSerializer
, use the following code to register it as a value serializer at application startup.
BsonSerializer.RegisterSerializer(typeof(Dictionary<string, object>),
new DictionaryInterfaceImplementerSerializer<Dictionary<string, object>, string, object>(
DictionaryRepresentation.Document,
new StringSerializer(),
new DynamicValueSerializer()));
Please note that this has to be done for each actual type, whose values it should be used for. The serializer registration cannot be done for interfaces. Thus, if SortedDictionary<string, object>
or SortedList<string, object>
are to be treated in the same way as Dictionary<string, object>
, the registration needs to be repeated for those types. (This affects whether dictionaries contained as values in collections of those types will be serialized without _t/_v, not necessarily whether the objects of those types themselves will be serialized in that manner.)
To apply the same serialization principle to List<object>
objects, use the following registration code.
BsonSerializer.RegisterSerializer(typeof(List<object>),
new EnumerableInterfaceImplementerSerializer<List<object>, object>(
new DynamicValueSerializer()));