[JAVA-5304] bson-kotlinx & polymorphic MongoCollection instances Created: 31/Jan/24  Updated: 05/Feb/24

Status: Scheduled
Project: Java Driver
Component/s: Kotlin
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Ross Lawley Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
Duplicate
is duplicated by JAVA-5309 Support polymorphic serialization in ... Closed
Assigned Teams:
Java Drivers

 Description   

The following code shows a bug deserializing data into a MongoCollection

 
@Serializable
sealed interface Pet {
    val name: String
}
 
@Serializable
data class Dog(
    @Contextual
    @SerialName("_id")
    val id: ObjectId,
    override val name: String,
    val favoriteToy: String,
) : Pet
 
@Serializable
data class Cat(
    @Contextual
    @SerialName("_id")
    val id: ObjectId,
    override val name: String,
    val favoritePlace: String,
) : Pet
 
suspend fun main() {
    val winston = Dog(ObjectId(), "Winston", "Ball")
 
    val collection = MongoClient.create().getDatabase("test").getCollection<Pet>("Pet")
    collection.insertOne(winston)
 
    collection.withDocumentClass<Document>().find().first().also { println(it) }
    collection.withDocumentClass<Dog>().find().first().also { println(it) }
    collection.withDocumentClass<Pet>().find().first().also { println(it) }  // FAILS with a codec error:
 
 
Stack trace:
Exception in thread "main" org.bson.BsonInvalidOperationException: Reading field '_id' failed expected STRING type but found: OBJECT_ID.
	at org.bson.codecs.kotlinx.DefaultBsonDecoder.decodeString(BsonDecoder.kt:302)
	at kotlinx.serialization.encoding.AbstractDecoder.decodeStringElement(AbstractDecoder.kt:58)
	at kotlinx.serialization.internal.AbstractPolymorphicSerializer.deserialize(AbstractPolymorphicSerializer.kt:52)
	at org.bson.codecs.kotlinx.KotlinSerializerCodec.decode(KotlinSerializerCodec.kt:182)
 
}



 Comments   
Comment by Ross Lawley [ 31/Jan/24 ]

When the discriminator is not the first field (eg its been written to MongoDB (which puts _id first) then the codec fails:

    val oid = "oid"
    val document = BsonDocument.parse("""{"_id": {"$oid": "65ba255334467b4eedee02af"}, "_t": "example.Dog", "name": "Winston", "favoriteToy": "Ball"}""")
    val decoded = codec.decode(BsonDocumentReader(document), DecoderContext.builder().build())
    println(decoded)

The above fails with the same error.

Comment by Ross Lawley [ 31/Jan/24 ]

Whats strange is the Pet codec can handle the instances when manually encoding / decoding:

    val codecRegistry = MongoClients.getDefaultCodecRegistry()
 
    val document = BsonDocument()
    val writer = BsonDocumentWriter(document)
    val codec = codecRegistry.get(Pet::class.java)
    codec.encode(writer, winston, EncoderContext.builder().build())
    writer.flush()
    println(document.toJson())
 
 
    val decoded = codec.decode(BsonDocumentReader(document), DecoderContext.builder().build())
    println(decoded)

Outputs:

{"_t": "example.Dog", "_id": {"$oid": "65ba255334467b4eedee02af"}, "name": "Winston", "favoriteToy": "Ball"}
Dog(id=65ba255334467b4eedee02af, name=Winston, favoriteToy=Ball)

(note the discriminator is the first field)

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