Some applications of MongoDB don't have need of the intermediate map structure provided by Document. Of those, some just need to shuttle around raw BSON, and for that there is RawBsonDocument. Others just want to traffic in JSON, and the best we can do for them now is a bit heavyweight: decode to Document and call its toJson method. This has two problems
- Applications have to pay for an intermediate Map that they don't need
- Applications wishing to customize via JsonWriterSettings have to pass the settings as a parameter to each toJson call.
The first problem can be alleviated by decoding to RawBsonDocument instead, but
- That's not so intuitive
- It results in an extra copy of the raw bytes
We can do better by introducing a new class that wraps a JSON string, and a Codec for it that is customizable with its own JsonWriterSettings. It would look something like this:
// Simple wrapping class public class JsonObject { private final String json; public JsonObject(final String json) { this.json = json; } public String getJson() { return json; } @Override public String toString() { return json; } }
// Codec for JsonObject public class JsonObjectCodec implements Codec<JsonObject> { private final JsonWriterSettings writerSettings; public JsonStringCodec() { this(JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build()); } public JsonStringCodec(final JsonWriterSettings writerSettings) { this.writerSettings = writerSettings; } @Override public JsonObject decode(final BsonReader bsonReader, final DecoderContext decoderContext) { StringWriter stringWriter = new StringWriter(); new JsonWriter(stringWriter, writerSettings).pipe(bsonReader); return new JsonString(stringWriter.toString()); } @Override public void encode(final BsonWriter bsonWriter, final JsonObject jsonObject, final EncoderContext encoderContext) { bsonWriter.pipe(new JsonReader(jsonString.getJson())); } @Override public Class<JsonObject> getEncoderClass() { return JsonObject.class; } }
If we add this to the default codec registry, then applications can just do:
var coll = client.getDatabase("test").getCollection("test", JsonString.class); // res is of type List<JsonString> var res = coll.find().into(new ArrayList<>());
To customize the Codec, just register your own instance in the registry:
MongoClientSettings.builder()
.codecRegistry(fromRegistries(
fromCodecs(
new JsonObjectCodec(JsonWriterSettings
.builder()
.outputMode(JsonMode.RELAXED)
.objectIdConverter((objectId, strictJsonWriter) -> {
strictJsonWriter.writeString(objectId.toHexString());
})
.build())),
getDefaultCodecRegistry()))