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.