Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way for creating Mongo codecs automatically?

I'm willing to migrate my code from mongojack to something that supports the new async mongo driver. Yet I find that the new way of encoding/decoding is through Codecs and I don't see myself writing a Codec for every class in my model. That's why I'd rather write a lib that given a class creates a Codec. However I don't know how, nor do I know if there's already some effort out there trying to achieve the same. Are there some libs that achieve what I want? if not, what's the best approach to achieve it.

(I know I should probably use CodecProvider somewhere in there, but I still don't know where to start)

like image 584
Alejandro Navas Avatar asked Mar 15 '23 20:03

Alejandro Navas


2 Answers

Here's how we address this (end result is super slick between Lombok, Jackson and MongoDB):

Provider:

public class JacksonCodecProvider implements CodecProvider {
    private final ObjectMapper objectMapper;

    public JacksonCodecProvider(final ObjectMapper bsonObjectMapper) {
        this.objectMapper = bsonObjectMapper;
    }

    @Override
    public <T> Codec<T> get(final Class<T> type, final CodecRegistry registry) {

            return new JacksonCodec<>(objectMapper, registry, type);

    }
}

And the Codec itself:

class JacksonCodec<T> implements Codec<T> {
    private final ObjectMapper objectMapper;
    private final Codec<RawBsonDocument> rawBsonDocumentCodec;
    private final Class<T> type;

    public JacksonCodec(ObjectMapper objectMapper,
                        CodecRegistry codecRegistry,
                        Class<T> type) {
        this.objectMapper = objectMapper;
        this.rawBsonDocumentCodec = codecRegistry.get(RawBsonDocument.class);
        this.type = type;
    }

    @Override
    public T decode(BsonReader reader, DecoderContext decoderContext) {
        try {

            RawBsonDocument document = rawBsonDocumentCodec.decode(reader, decoderContext);
            String json = document.toJson();
            return objectMapper.readValue(json, type);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public void encode(BsonWriter writer, Object value, EncoderContext encoderContext) {
        try {

            String json = objectMapper.writeValueAsString(value);

            rawBsonDocumentCodec.encode(writer, RawBsonDocument.parse(json), encoderContext);

        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public Class<T> getEncoderClass() {
        return this.type;
    }
}

When combined with Lombok and the latest Jackson annotations, it allows us to do stuff like this (hardly looks like Java code, eh?):

@JsonIgnoreProperties(ignoreUnknown=true)
@JsonDeserialize(builder = Account.AccountBuilder.class)
@Builder(toBuilder=true)
@Value
public class Account {

    @JsonProperty private String _id;
    @JsonProperty private long _version;
    @JsonProperty private String organizationName;

    @JsonPOJOBuilder(withPrefix = "")
    public static final class AccountBuilder {
    }

}

Then:

Account account = collection.find(eq("_id", id)).first();
System.out.println(account.getOrganizationName());
like image 61
Kevin Day Avatar answered Mar 17 '23 17:03

Kevin Day


Yes, if you use Jackson you can use the mongo-jackson-codec from https://github.com/ylemoigne/mongo-jackson-codec which will handle it automatically for you.

like image 20
mkmelin Avatar answered Mar 17 '23 16:03

mkmelin