Andrei Barbolin
Andrei Barbolin

Reputation: 435

Custom serializer that processes both nullable and not nullable types for MongoDb

I need to implement custom serializer using IBsonSerializer.

That's what I did:

internal class MyCustomDateTimeSerializer : IBsonSerializer
{
   public object Deserialize(BsonDeserializationContext context, 
         BsonDeserializationArgs args)
   {
       // Deserialization logic
   }

   public void Serialize(BsonSerializationContext context, 
         BsonSerializationArgs args, object value)
   {
       // Serialization logic
   }

   public Type ValueType => typeof(DateTime);
}

And then use it in BsonSerializerAttribute:

[BsonSerializer(typeof(MyCustomDateTimeSerializer))]

My problem is that I want to Serialize/Deserialize both DateTime and Nullable DateTime.

ValueType of my CustomSerializer set to typeof(DateTime), because of that I get the exception like:

Value type of serializer is System.DateTime and does not match member type System.Nullable`1[[System.DateTime..

I did not find any solutions for this issue. Of course, I can create two different classes for Nullable DateTime and DateTime, but maybe there is another option?

Upvotes: 5

Views: 3179

Answers (1)

CodeFuller
CodeFuller

Reputation: 31312

If you check the source code of SetSerializer method in MongoDB.Bson library, you will see that it has pretty straightforward check for member type:

if (serializer.ValueType != _memberType)
{
    var message = string.Format("Value type of serializer is {0} and does not match member type {1}.", serializer.ValueType.FullName, _memberType.FullName);
    throw new ArgumentException(message, "serializer");
}

The code checks type equality, and there is no way to trick it and make think that DateTime and Nullable<DateTime> are equal.

However there is a solution that you could use to have one serializer class and avoid duplicating the code. You could make your serializer a generic class and force it to accept only DateTime or Nullable<DateTime> as type argument. Here is a sample:

internal class MyCustomDateTimeSerializer<TDateTime> : IBsonSerializer
{
    static MyCustomDateTimeSerializer()
    {
        if (typeof(TDateTime) != typeof(DateTime) && typeof(TDateTime) != typeof(DateTime?))
        {
            throw new InvalidOperationException($"MyCustomDateTimeSerializer could be used only with {nameof(DateTime)} or {nameof(Nullable<DateTime>)}");
        }
    }

    public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        // Deserialization logic
    }

    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
    {
        // Serialization logic
    }

    public Type ValueType => typeof(TDateTime);
}

public class SomeDocument
{
    // ...

    [BsonSerializer(typeof(MyCustomDateTimeSerializer<DateTime>))]
    public DateTime Date1 { get; set; }

    [BsonSerializer(typeof(MyCustomDateTimeSerializer<DateTime?>))]
    public DateTime? Date2 { get; set; }
}

Upvotes: 5

Related Questions