Uploaded image for project: 'Java Driver'
  1. Java Driver
  2. JAVA-5134

Kotlin driver : Data class with nullable type throw BsonInvalidOperationException when value is explicitly null in the DB

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 4.11.0
    • Affects Version/s: 4.10.2
    • Component/s: BSON, Codecs, Kotlin
    • None
    • Not Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      Summary

      Hello,

      I’m using :

      -> Kotlin driver => org.mongodb:mongodb-driver-kotlin-sync:4.10.2
      (the same issue exists in org.mongodb:mongodb-driver-kotlin-coroutine:4.10.1 too)

      -> MongoDB Atlas Dedicated Cluster M10 v6.1

       

      I have a data class with a nullable String field

      {{data class MyData( val myfield: String?
      )}}

      When I read the value from the DB, here are what is printed on console :

      1. if myfield does NOT exist in the DB => it successfully prints “MyData(myfield=null)”
      1. if myfield exists and contains “myvalue” => it successfully prints “MyData(myfield=myvalue)”
      1. if myfield exists and contains null value (BSONType is NULL) => I would expect that it prints “MyData(myfield=null)” but it is not!? It throws the BsonInvalidOperationExceptionreadString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is NULL.
        Could you please tell me what’s wrong? Thanks!

      How to Reproduce

      Here is a very simple code to reproduce (replace XXX, YYY, ZZZ with your values)

       

      data class MyData(
        val myfield: String?
      )
      fun main(args: Array<String>) {
        val uri = "XXXXXXXXX"
        val databaseName = "YYYYYYYY"
        val collectionName = "ZZZZZZZ"
        val mongoClient = MongoClient.create(uri)
        val db = mongoClient.getDatabase(databaseName)
        val collection = db.getCollection<MyData>(collectionName)
        val doc = collection.find().firstOrNull()
        if (doc != null) {
          println(doc)
        } else {
          println("No matching documents found.")
        }
        mongoClient.close()
      }
      

       

      Additional Background

      Here is the exception trace

       

      Exception in thread "main" org.bson.codecs.configuration.CodecConfigurationException: Unable to decode myfield for MyData data class.	at org.bson.codecs.kotlin.DataClassCodec.decode(DataClassCodec.kt:92)	at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)	at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)	at org.bson.internal.LazyCodec.decode(LazyCodec.java:53)	at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:104)	at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:87)	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:42)	at com.mongodb.internal.connection.ReplyMessage.<init>(ReplyMessage.java:48)	at com.mongodb.internal.connection.InternalStreamConnection.getCommandResult(InternalStreamConnection.java:565)	at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:455)	at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:370)	at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:114)	at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:719)	at com.mongodb.internal.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:76)	at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:203)	at com.mongodb.internal.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:115)	at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:83)	at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:74)	at com.mongodb.internal.connection.DefaultServer$OperationCountTrackingConnection.command(DefaultServer.java:287)	at com.mongodb.internal.operation.CommandOperationHelper.createReadCommandAndExecute(CommandOperationHelper.java:245)	at com.mongodb.internal.operation.FindOperation.lambda$execute$1(FindOperation.java:324)	at com.mongodb.internal.operation.OperationHelper.lambda$withSourceAndConnection$0(OperationHelper.java:345)	at com.mongodb.internal.operation.OperationHelper.withSuppliedResource(OperationHelper.java:370)	at com.mongodb.internal.operation.OperationHelper.lambda$withSourceAndConnection$1(OperationHelper.java:344)	at com.mongodb.internal.operation.OperationHelper.withSuppliedResource(OperationHelper.java:370)	at com.mongodb.internal.operation.OperationHelper.withSourceAndConnection(OperationHelper.java:343)	at com.mongodb.internal.operation.FindOperation.lambda$execute$2(FindOperation.java:321)	at com.mongodb.internal.operation.CommandOperationHelper.lambda$decorateReadWithRetries$3(CommandOperationHelper.java:192)	at com.mongodb.internal.async.function.RetryingSyncSupplier.get(RetryingSyncSupplier.java:67)	at com.mongodb.internal.operation.FindOperation.execute(FindOperation.java:332)	at com.mongodb.internal.operation.FindOperation.execute(FindOperation.java:72)	at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:153)	at com.mongodb.client.internal.FindIterableImpl.first(FindIterableImpl.java:213)	at com.mongodb.kotlin.client.MongoIterable.firstOrNull(MongoIterable.kt:38)	at MainKt.main(Main.kt:20)Caused by: org.bson.BsonInvalidOperationException: readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is NULL.	at org.bson.AbstractBsonReader.verifyBSONType(AbstractBsonReader.java:689)	at org.bson.AbstractBsonReader.checkPreconditions(AbstractBsonReader.java:721)	at org.bson.AbstractBsonReader.readString(AbstractBsonReader.java:456)	at org.bson.codecs.StringCodec.decode(StringCodec.java:80)	at org.bson.codecs.StringCodec.decode(StringCodec.java:31)	at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:96)	at org.bson.codecs.kotlin.DataClassCodec.decode(DataClassCodec.kt:90)	... 37 more 

       

      Here is my build.gradle.kts

      plugins {
          kotlin("jvm") version "1.9.10"
          application
      }
      
      group = "org.example"
      version = "1.0-SNAPSHOT"
      
      repositories {
          mavenCentral()
      }
      
      dependencies {
          testImplementation(kotlin("test"))
          implementation("org.mongodb:mongodb-driver-kotlin-sync:4.10.2")
          //implementation("org.mongodb:mongodb-driver-kotlin-coroutine:4.10.1")
      }
      
      tasks.test {
          useJUnitPlatform()
      }
      
      kotlin {
          jvmToolchain(8)
      }
      
      application {
          mainClass.set("MainKt")
      }

      Thanks for your help!

       

            Assignee:
            ross@mongodb.com Ross Lawley
            Reporter:
            sebastien.perochon@mobiquite.fr Sébastien Pérochon
            Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: