DateOnly mapping

Hi everyone!

The release of MongoDB .NET Driver 3.2.0 introduces support for the DateOnly type.
After upgrading to this version, we are no longer able to deserialize DateOnly values.

Upon investigating the change introduced in CSHARP-5345, I noticed that DateOnly values are now serialized using a hardcoded YearMonthDay representation as follows:

bsonWriter.WriteInt32("Year", value.Year);
bsonWriter.WriteInt32("Month", value.Month);
bsonWriter.WriteInt32("Day", value.Day);

As you can see, the property names “Year”, “Month”, and “Day” are in PascalCase, whereas our current implementation serializes these properties in camelCase, causing deserialization to fail consistently.

Does anyone know of a workaround other than writing a custom serializer for DateOnly types?

Thanks!

Hi @Matias_Paulo,

Welcome to the forum :smile:
We introduced a serializer for DateOnly in version 3.0. Before that there was no specific serializer, so the BsonClassMapSerializer was being used and it was serializing DateOnly as { Year : x, Month : x, Day : x }.

In order to keep compatibility with versions 2.x, in version 3.2.0 we decided to add the BsonDateOnlyOptions attribute that allows to configure the way DateOnly should be serialized: DateOnlyDocumentFormat.YearMonthDay that uses the year/month/day format (as it was done in versions 2.x), and DateOnlyDocumentFormat.DateTimeTicks that uses the dateTime/tick format (the same that is used for DateTime serialization) and that is used by default.

I think that probably in your code you must be using CamelCaseElementNameConvention, so the name of the properties serialized with the BsonClassMapSerializer were serialized using camel case convention, while the new DateOnlySerializer ignores the convention.

In our case what I would do is use the BsonClassMapSerializer for DateOnly as before, so you can keep the same behaviour you had so far:

var dateOnlyClassMap = new BsonClassMap<DateOnly>(cm => cm.AutoMap()).Freeze();
var dateOnlyClassMapSerializer = new BsonClassMapSerializer<DateOnly>(dateOnlyClassMap);
BsonSerializer.RegisterSerializer<DateOnly>(dateOnlyClassMapSerializer);

I hope this makes sense and let me know how it goes.

Hi @papafe !! Thanks for your response!

We solve the issue by implementing our own custom serializer for the DateOnly type.
Something like:

public sealed class CustomDateOnlySerializer : IBsonSerializer<DateOnly>
{
    /// <inheritdoc />
    public Type ValueType => typeof(DateOnly);

    /// <inheritdoc />
    public DateOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        var reader = context.Reader;
        reader.ReadStartDocument();

        var year = reader.ReadInt32();
        var month = reader.ReadInt32();
        var day = reader.ReadInt32();

        reader.ReadEndDocument();

        return new DateOnly(year, month, day);
    }

    /// <inheritdoc />
    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateOnly value)
    {
        var writer = context.Writer;
        writer.WriteStartDocument();

        writer.WriteName("year");
        writer.WriteInt32(value.Year);

        writer.WriteName("month");
        writer.WriteInt32(value.Month);

        writer.WriteName("day");
        writer.WriteInt32(value.Day);

        writer.WriteEndDocument();
    }
}

I’m not sure if it’s the best approach, as it might impact performance, but at least it works.
My question is if we might encounter more issues related to the naming convention for different types in the future…

Nevertheless, I appreciate your time, and if you have a way to optimize my code, I would really appreciate it.

Thanks!

Hi @Matias_Paulo, glad that you found a solution :wink:

So, in general implementing your own serializer won’t impact performance, as it’s also what we do internally. We instantiate serializers and register them in the same way you can do it through the public API, so nothing to worry about.
That said, my major doubt about your serializer is the Deserialize method. The issue there is that you expect that the elements are exactly in the year/month/day order, and maybe they are not. What I would suggest here is to look at our own implementation of the DateOnlySerializer here. In particular you can see how we use the _helper in Deserialize to be robust to different ordering of the fields. Overall I’d say that if you want to implement your own serializer you could just copy it and then remove all the parts you don’t need. Our implementation supports multiple representation and formats, so you can probably remove most of it.
Hope this was clear enough.
Otherwise you can use the BsonClassMap as I’ve shown you before :wink:

Thanks @papafe for your suggestions! I’ll implement them.

Best regards.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.