Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB custom serializer implementation

I am new to MongoDB, and am trying to get the C# driver to work serializing F# classes. I have it working with the class automapper using mutable F# fields & a parameterless constructor, but really I need to retain immutability, so I started looking at implementing an IBsonSerializer to perform custom serialization. I haven't found any documentation for writing one of these so have just tried to infer from the driver source code.

I have run into a problem whereby when the Deserialize method is called on the serializer, the CurrentBsonType is set to EndOfDocument rather than the start as I am expecting. I wrote the equivalent in C# just to make sure it wasn't some F# weirdness, but the problem persists. The serialization part seems to work fine and is queryable from the shell. Here is the sample code:

class Calendar {
    public string Id { get; private set; }
    public DateTime[] Holidays { get; private set; }

    public Calendar(string id, DateTime[] holidays) {
        Id = id;
        Holidays = holidays;
    }
}

class CalendarSerializer : BsonBaseSerializer {
    public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) {
        var calendar = (Calendar) value;
        bsonWriter.WriteStartDocument();
        bsonWriter.WriteString("_id", calendar.Id);
        bsonWriter.WriteName("holidays");
        var ser = new ArraySerializer<DateTime>();
        ser.Serialize(bsonWriter, typeof(DateTime[]), calendar.Holidays, null);
        bsonWriter.WriteEndDocument();
    }

    public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) {
        if (nominalType != typeof(Calendar) || actualType != typeof(Calendar))
            throw new BsonSerializationException();

        if (bsonReader.CurrentBsonType != BsonType.Document)
            throw new FileFormatException();

        bsonReader.ReadStartDocument();
        var id = bsonReader.ReadString("_id");
        var ser = new ArraySerializer<DateTime>();
        var holidays = (DateTime[])ser.Deserialize(bsonReader, typeof(DateTime[]), null);
        bsonReader.ReadEndDocument();
        return new Calendar(id, holidays);
    }

    public override bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) {
        var calendar = (Calendar) document;
        id = calendar.Id;
        idNominalType = typeof (string);
        idGenerator = new StringObjectIdGenerator();
        return true;
    }

    public override void SetDocumentId(object document, object id) {
        throw new NotImplementedException("SetDocumentId is not implemented");
    }
}

This blows up with FileFormatException in Deserialize when the CurrentBsonType is not Document. I am using the latest version 1.4 of the driver source.

like image 643
fhusb Avatar asked Mar 20 '12 14:03

fhusb


1 Answers

I figured this out in the end. I should have used bsonReader.GetCurrentBsonType() instead of bsonReader.CurrentBsonType. This reads the BsonType in from the buffer rather than just looking at the last thing there. I also fixed a subsequent bug derserializing. The updated method looks like this:

public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) {
    if (nominalType != typeof(Calendar) || actualType != typeof(Calendar))
        throw new BsonSerializationException();

    if (bsonReader.GetCurrentBsonType() != BsonType.Document)
        throw new FileFormatException();

    bsonReader.ReadStartDocument();
    var id = bsonReader.ReadString("_id");
    bsonReader.ReadName();
    var ser = new ArraySerializer<DateTime>();
    var holidays = (DateTime[])ser.Deserialize(bsonReader, typeof(DateTime[]), null);
    bsonReader.ReadEndDocument();
    return new Calendar(id, holidays);
}
like image 146
fhusb Avatar answered Oct 09 '22 14:10

fhusb