MongoDB BSON codec not being used while encoding object MongoDB BSON codec not being used while encoding object mongodb mongodb

MongoDB BSON codec not being used while encoding object


After several days of research, I've figured out a solution.

The DutyBlockCodec depends on the LocalDateCodec (which I created) in order to encode/decode. This dependency isn't satisfied just by adding the two codecs into the same codec registry. The solution is to pass a CodecRegistry object containing the codecs that DutyBlockCodec depends on (e.g. a CodecRegistry containing within it the LocalDateCodec) to the DutyBlockCodec's constructor, which is stored as a member variable. In order to use the LocalDateCodec to encode, I use the EncoderContext.encodeWithChildContext() method, passing in the codec, writer, and element to encode. Additionally, I write individual fields rather than writing a Document as a String (as in my original code). Thus the DutyBlock codec ends up looking like this:

public class DutyBlockCodec implements Codec<DutyBlock> {    private final CodecRegistry codecRegistry;    public DutyBlockCodec(final CodecRegistry codecRegistry) {        this.codecRegistry = codecRegistry;    }    @Override    public void encode(BsonWriter writer, DutyBlock t, EncoderContext ec) {        writer.writeStartDocument();            Codec dateCodec = codecRegistry.get(LocalDate.class);            writer.writeName("startDate");            ec.encodeWithChildContext(dateCodec, writer, t.getStartDate());            writer.writeName("endDate");            ec.encodeWithChildContext(dateCodec, writer, t.getEndDate());            writer.writeName("blockLength");            writer.writeInt32(t.getBlockLength());            writer.writeName("pointValue");            writer.writeDouble(t.getPointValue());            //Writing ArrayList of RAs            writer.writeName("assigned");            writer.writeStartArray();                for (Ra ra : t.getRasOnDuty()) {                    Codec raCodec = codecRegistry.get(Ra.class);                    ec.encodeWithChildContext(raCodec, writer, ra);                }            writer.writeEndArray();        writer.writeEndDocument();    }    @Override    public Class<DutyBlock> getEncoderClass() {        return DutyBlock.class;    }    @Override    public DutyBlock decode(BsonReader reader, DecoderContext dc) {        reader.readStartDocument();            Codec<LocalDate> dateCodec = codecRegistry.get(LocalDate.class);            reader.readName();            LocalDate startDate = dateCodec.decode(reader, dc);            reader.readName();            LocalDate endDate = dateCodec.decode(reader, dc);            reader.readName();            int blockLength = reader.readInt32();            reader.readName();            double pointValue = reader.readDouble();            //Reading ArrayList of RAs            reader.readName();            Codec<Ra> raCodec = codecRegistry.get(Ra.class);            ArrayList<Ra> rasOnDuty = new ArrayList<>();            reader.readStartArray();                while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {                    rasOnDuty.add(raCodec.decode(reader, dc));                }            reader.readEndArray();        reader.readEndDocument();        return new DutyBlock(startDate, endDate, blockLength, pointValue, rasOnDuty);    }}

DutyBlockCodec depends on another codec, and so requires a CodecRegistry to be passed in on its constructor. While I believe it is possible to create a CodecRegistry with the LocalDateCodec, then pass this as an argument to DutyBlockCodec's constructor, then create another CodecRegistry containing both LocalDateCodec and DutyBlockCodec, this is rather confusing, and MongoDB provides a functionality, the CodecProvider to facilitate this process.

Using the CodecProvider interface, I wrote a DutyBlockCodecProvider

public class DutyBlockCodecProvider implements CodecProvider {    @Override    public <T> Codec<T> get(Class<T> type, CodecRegistry cr) {        if (type == DutyBlock.class) {            return (Codec<T>) new DutyBlockCodec(cr);        }        return null;    }}

I added these CodecProviders to the MongoDB Client using the CodecRegistries.fromProviders() method.

CodecRegistry codecRegistry = CodecRegistries.fromRegistries(            CodecRegistries.fromCodecs(new LocalDateCodec()),            CodecRegistries.fromProviders(                    new RaCodecProvider(),                    new DutyBlockCodecProvider(),                    new ScheduledDutyCodecProvider()),            MongoClient.getDefaultCodecRegistry());      MongoClientOptions options = MongoClientOptions.builder()            .codecRegistry(codecRegistry).build();    mongoClient = new MongoClient(new ServerAddress(), options);    db = mongoClient.getDatabase("DutySchedulerDB");

My source code for this project can be found at https://github.com/desrepair/DutySchedulerI'm open to answering any questions people may have.


Since the version 3.7 of the mongo java driver, Instant, LocalDate and LocalDateTime are supported. You don't need to code your own codec anymore.