[JAVA-3677] Support BsonRepresentation annotation on POJOs Created: 02/Apr/20  Updated: 28/Oct/23  Resolved: 17/Sep/20

Status: Closed
Project: Java Driver
Component/s: POJO
Affects Version/s: None
Fix Version/s: 4.2.0

Type: New Feature Priority: Major - P3
Reporter: Dmitrii Volykhin Assignee: Brian DeLeonardis (Inactive)
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Documented
Documentation Changes: Needed
Documentation Changes Summary:

See the changes in the commit to the .md file.


 Description   

Original Description:

Motivation: clean POJO class do not use ObjectId in field. Pojo class do not know about mongodb logic.

Add opportunity to serialize/deserilize document field `ObjectId _id` to `String _id` in POJO field.

public static class TestModel {
    public String _id; // ObjectId.toHexString()
    public int id;
    public String name;
}

Now there is an exception: Failed to decode 'TestModel'. Decoding '_id' errored with: readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is OBJECT_ID.

Reproducer: http://klikr.org/869dbe58dd5df1a38b9a968c8271.txt
Exception http://klikr.org/4386afc3820d7f89a218f98c2d79.txt

Description after Implementation:

To implement this, we ended up adding a BsonRepresentation annotation. This makes it easy to specify that we want to store the field as one type in the database, but convert it to a different type when used in Java POJOs. To achieve what the above example was describing, one can now do:

public class TestModel {
    @BsonRepresentation(BsonType.ObjectId)    
    public String _id;
    public String name;    ...
}

 



 Comments   
Comment by Githook User [ 17/Sep/20 ]

Author:

{'name': 'Brian DeLeonardis', 'email': 'bdeleonardis1@gmail.com', 'username': 'bdeleonardis1'}

Message: Add BsonRepresentation to allow ObjectId to String conversion

JAVA-3677
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/0f6292d2993ac1b88b688dc9fbea35ea568cb525

Comment by Jeffrey Yemin [ 31/Aug/20 ]

We agreed that we should take a similar approach as the .NET driver and implement a BsonRepresentation annotation, with associated support in relevant Codec implementations, in particular StringCodec.

Comment by Andreas Keefer [ 29/Jun/20 ]

I would also love to see this feature!

I implemented a custom Type to walk around this via a Codec. My Usecase is a bit different. I have two applications, one writeing _id as ObjectId and the other as String (UUID):

/**
* A mapped Entity to persist in a Mongodb collection
*/
data class User(
        @BsonId
        var id: UserId,
 
        @NotBlank
        var username: String        //....)
 
/**
* A typed ID Class
*/
data class UserId(var id: String = UUID.randomUUID().toString()) {
    constructor(id: ObjectId) : this(id.toString())
 
    override fun toString() = id
}
 
/**
* Codec to map between Java and MongoDb datatypes
*/
class UserIdCodec : Codec<UserId> {
    override fun getEncoderClass() = UserId::class.java
 
    override fun encode(writer: BsonWriter, value: UserId, encoderContext: EncoderContext) =
            writer.writeString(value.id)
 
    override fun decode(reader: BsonReader, decoderContext: DecoderContext) =
            when (reader.currentBsonType) {
                BsonType.OBJECT_ID -> UserId(reader.readObjectId())
                BsonType.STRING -> UserId(reader.readString())
                else -> throw IllegalStateException("unsupported BsonType: ${reader.currentBsonType}")
            }
}
/**
* CodecProvider to register the codec
* /
class ObjectIdCodecProvider : CodecProvider {
    override fun <T : Any?> get(clazz: Class<T>?, registry: CodecRegistry?): Codec<T>? {
        return if (clazz === UserId::class.java) {
            UserIdCodec() as Codec<T>
        } else null
    }
}

In your usecase the codec would look like this:

class UserIdCodec : Codec<UserId> {
    override fun getEncoderClass() = UserId::class.java
 
    override fun encode(writer: BsonWriter, value: UserId, encoderContext: EncoderContext) =
            writer.writeObjectId(ObjectId( value.id))
 
    override fun decode(reader: BsonReader, decoderContext: DecoderContext) =
            UserId(reader.readObjectId())
}

Generated at Thu Feb 08 09:00:10 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.