How to set a MongoDb Serializer to an inner object property

96 Views Asked by At

Is it possible for MongoDb to set a DateTimeSerializer for a C# inner object DataTime property in a POCO class using ClassMap without the BsonDateTimeOptions attribute ?

Example :

public class Entity
{
    public string Id { get; init; }
    
    public string Currency { get; init; }
    
    public IEnumerable<InnerEtity> InnerEtities { get; init;}
}
        
public class InnerEtity
{
    //Don't want to use this attribute
    [BsonDateTimeOptions(DateOnly = true)]
    public DateTime Date { get; set; }
        
    public double Value { get; set; }
}
3

There are 3 best solutions below

2
Markus On BEST ANSWER

You can use "imperative" mapping instead of using the attributes, e.g.

BsonClassMap.RegisterClassMap<InnerEtity>(classMap =>
{
    classMap.AutoMap();
    classMap.GetMemberMap(c => c.Date).SetSerializer(new DateTimeSerializer(dateOnly: true));
});

This way, you do not need to use attributes for the mapping and keep the class "MongoDB-agnostic". Please be aware that you need to run the code when bootstraping your application so that MongoDB C# Driver already knows the class map when using the entity for the first time.

0
dododo On

You can specify serialization options in this way:

        BsonClassMap.RegisterClassMap<InnerEtity>(classMap =>
        {
            classMap.AutoMap();
            classMap.GetMemberMap(c => c.Date).SetSerializer(new DateTimeSerializer(dateOnly: true));
        });
0
Hadi Mohagheghi On

I think your problem can be solved like this:

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
using System;
using System.Collections.Generic;

public class Entity
{
    public string Id { get; init; }
    
    public string Currency { get; init; }
    
    public IEnumerable<InnerEntity> InnerEntities { get; init; }
}
        
public class InnerEntity
{
    public DateTime Date { get; set; }
        
    public double Value { get; set; }
}

public class EntityClassMap : BsonClassMap<Entity>
{
    public override void RegisterClassMap(BsonClassMap<Entity> cm)
    {
        cm.AutoMap();
        cm.MapMember(x => x.InnerEntities).SetSerializer(new EnumerableSerializer<InnerEntity>());
    }
}

public class InnerEntityClassMap : BsonClassMap<InnerEntity>
{
    public override void RegisterClassMap(BsonClassMap<InnerEntity> cm)
    {
        cm.AutoMap();
        cm.MapMember(x => x.Date).SetSerializer(new CustomDateTimeSerializer());
    }
}

public class CustomDateTimeSerializer : IBsonSerializer<DateTime>
{
    public Type ValueType => typeof(DateTime);

    public DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        // your custom deserialization logic here 
        // if needed.
        return context.Reader.ReadDateTime();
    }

    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value)
    {
        // your custom serialization logic here 
        // if needed!
        context.Writer.WriteDateTime(value);
    }

    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
    {
        if (value == null)
        {
            context.Writer.WriteNull();
            return;
        }

        if (!(value is DateTime))
        {
            throw new ArgumentException("Value is not a DateTime.");
        }

        Serialize(context, args, (DateTime)value);
    }
}

class Program
{
    static void Main(string[] args)
    {
        BsonClassMap.RegisterClassMap<Entity>(cm => new EntityClassMap().RegisterClassMap(cm));
        BsonClassMap.RegisterClassMap<InnerEntity>(cm => new InnerEntityClassMap().RegisterClassMap(cm));

        //  MongoDB connection 
    }
}