Value classes in sealed polymorphic structures fail to serialize

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Kotlin
    • None
    • None
    • Java Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Here's example code:

      @Serializable
      sealed interface AnyToken {
        val value: String
      }
      
      @JvmInline
      @Serializable
      value class GenericToken(override val value: String) : AnyToken
      
      @Serializable
      data class TokenEntity(
        @SerialName("_id")
        val id: ObjectId,
        val token: AnyToken,
        val createdAt: Instant,
        val expiresAt: Instant,
      )

       

      If i try to save TokenEntity it will error with:

      org.bson.BsonInvalidOperationException: writeString can only be called when State is VALUE, not when State is NAME	at org.bson.AbstractBsonWriter.throwInvalidState(AbstractBsonWriter.java:748)	at org.bson.AbstractBsonWriter.checkPreconditions(AbstractBsonWriter.java:703)	at org.bson.AbstractBsonWriter.writeString(AbstractBsonWriter.java:608)	at com.mongodb.internal.connection.BsonWriterDecorator.writeString(BsonWriterDecorator.java:236)	at com.mongodb.internal.connection.IdHoldingBsonWriter.writeString(IdHoldingBsonWriter.java:366)	at org.bson.codecs.kotlinx.BsonEncoderImpl.encodeString(BsonEncoder.kt:186)	at dev.aaur1s.portaler.shared.model.vo.AccessToken$$serializer.serialize-b2Dk1lg(Tokens.kt:28)	at dev.aaur1s.portaler.shared.model.vo.AccessToken$$serializer.serialize(Tokens.kt:28)	at kotlinx.serialization.encoding.Encoder.encodeSerializableValue(Encoding.kt:280)	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableValue(AbstractEncoder.kt:18)	at org.bson.codecs.kotlinx.BsonEncoderImpl.access$encodeSerializableValue$s848091770(BsonEncoder.kt:66)	at org.bson.codecs.kotlinx.BsonEncoderImpl$encodeSerializableValue$2.invoke(BsonEncoder.kt:158)	at org.bson.codecs.kotlinx.BsonEncoderImpl$encodeSerializableValue$2.invoke(BsonEncoder.kt:149)	at org.bson.codecs.kotlinx.BsonEncoderImpl$DeferredElementHandler.with(BsonEncoder.kt:251)	at org.bson.codecs.kotlinx.BsonEncoderImpl.encodeSerializableValue(BsonEncoder.kt:149)	at org.bson.codecs.kotlinx.JsonBsonEncoder.encodeSerializableValue(JsonBsonEncoder.kt:62)	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableElement(AbstractEncoder.kt:80)	at kotlinx.serialization.internal.AbstractPolymorphicSerializer.serialize(AbstractPolymorphicSerializer.kt:35)	at kotlinx.serialization.encoding.Encoder.encodeSerializableValue(Encoding.kt:280)	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableValue(AbstractEncoder.kt:18)	at org.bson.codecs.kotlinx.BsonEncoderImpl.access$encodeSerializableValue$s848091770(BsonEncoder.kt:66)	at org.bson.codecs.kotlinx.BsonEncoderImpl$encodeSerializableValue$2.invoke(BsonEncoder.kt:158)	at org.bson.codecs.kotlinx.BsonEncoderImpl$encodeSerializableValue$2.invoke(BsonEncoder.kt:149)	at org.bson.codecs.kotlinx.BsonEncoderImpl$DeferredElementHandler.with(BsonEncoder.kt:251)	at org.bson.codecs.kotlinx.BsonEncoderImpl.encodeSerializableValue(BsonEncoder.kt:149)	at org.bson.codecs.kotlinx.JsonBsonEncoder.encodeSerializableValue(JsonBsonEncoder.kt:62)	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableElement(AbstractEncoder.kt:80)	at dev.aaur1s.portaler.shared.model.entity.Token.write$Self$Shared(Token.kt:11)	at dev.aaur1s.portaler.shared.model.entity.Token$$serializer.serialize(Token.kt:11)	at dev.aaur1s.portaler.shared.model.entity.Token$$serializer.serialize(Token.kt:11)	at org.bson.codecs.kotlinx.KotlinSerializerCodec.encode(KotlinSerializerCodec.kt:177)	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)	at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.lambda$encode$0(SplittablePayload.java:221)	at java.base/java.util.HashMap.compute(HashMap.java:1324)	at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.encode(SplittablePayload.java:211)	at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.encode(SplittablePayload.java:199)	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)	at com.mongodb.internal.connection.BsonWriterHelper.encodeUsingRegistry(BsonWriterHelper.java:186)	at com.mongodb.internal.connection.BsonWriterHelper.writeDocument(BsonWriterHelper.java:172)	at com.mongodb.internal.connection.BsonWriterHelper.writePayload(BsonWriterHelper.java:84)	at com.mongodb.internal.connection.CommandMessage.writeOpMsg(CommandMessage.java:252)	at com.mongodb.internal.connection.CommandMessage.encodeMessageBody(CommandMessage.java:234)	at com.mongodb.internal.connection.RequestMessage.encode(RequestMessage.java:95)	at com.mongodb.internal.connection.CommandMessage.encode(CommandMessage.java:73)	at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveAsyncInternal(InternalStreamConnection.java:555)	at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendAndReceiveAsync$1(InternalStreamConnection.java:392)	at com.mongodb.internal.async.AsyncSupplier.finish(AsyncSupplier.java:68)	at com.mongodb.internal.async.AsyncSupplier.getAsync(AsyncSupplier.java:49)	at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendAndReceiveAsync$2(InternalStreamConnection.java:395)	at com.mongodb.internal.async.AsyncSupplier.finish(AsyncSupplier.java:68)	at com.mongodb.internal.async.AsyncRunnable.lambda$thenSupply$7(AsyncRunnable.java:218)	at com.mongodb.internal.async.SingleResultCallback.complete(SingleResultCallback.java:63)	at com.mongodb.internal.async.AsyncRunnable.lambda$beginAsync$0(AsyncRunnable.java:123)	at com.mongodb.internal.async.AsyncRunnable.lambda$thenSupply$8(AsyncRunnable.java:216)	at com.mongodb.internal.async.AsyncSupplier.finish(AsyncSupplier.java:68)	at com.mongodb.internal.async.AsyncSupplier.lambda$onErrorIf$8(AsyncSupplier.java:171)	at com.mongodb.internal.async.AsyncSupplier.finish(AsyncSupplier.java:68)	at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveAsync(InternalStreamConnection.java:398)	at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceiveAsync(UsageTrackingInternalConnection.java:141)	at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceiveAsync(DefaultConnectionPool.java:782)	at com.mongodb.internal.connection.CommandProtocolImpl.executeAsync(CommandProtocolImpl.java:66)	at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.executeAsync(DefaultServer.java:230)	at com.mongodb.internal.connection.DefaultServerConnection.executeProtocolAsync(DefaultServerConnection.java:119)	at com.mongodb.internal.connection.DefaultServerConnection.commandAsync(DefaultServerConnection.java:100)	at com.mongodb.internal.connection.DefaultServer$AsyncOperationCountTrackingConnection.commandAsync(DefaultServer.java:368)	at com.mongodb.internal.operation.MixedBulkWriteOperation.executeCommandAsync(MixedBulkWriteOperation.java:444)	at com.mongodb.internal.operation.MixedBulkWriteOperation.lambda$executeBulkWriteBatchAsync$8(MixedBulkWriteOperation.java:331)	at com.mongodb.internal.async.function.AsyncCallbackLoop.run(AsyncCallbackLoop.java:56)	at com.mongodb.internal.operation.MixedBulkWriteOperation.executeBulkWriteBatchAsync(MixedBulkWriteOperation.java:365)	at com.mongodb.internal.operation.MixedBulkWriteOperation.lambda$executeAsync$4(MixedBulkWriteOperation.java:259)	at com.mongodb.internal.operation.AsyncOperationHelper.lambda$withAsyncSourceAndConnection$0(AsyncOperationHelper.java:121)	at com.mongodb.internal.operation.AsyncOperationHelper.lambda$withAsyncSuppliedResource$2(AsyncOperationHelper.java:138)	at com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$2(AsyncCallbackSupplier.java:97)	at com.mongodb.internal.operation.AsyncOperationHelper.lambda$withAsyncSuppliedResource$3(AsyncOperationHelper.java:139)	at com.mongodb.internal.connection.DefaultServer.lambda$getConnectionAsync$0(DefaultServer.java:127)	at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47)	at com.mongodb.internal.connection.DefaultConnectionPool.lambda$getAsync$0(DefaultConnectionPool.java:219)	at com.mongodb.internal.connection.DefaultConnectionPool.lambda$getAsync$1(DefaultConnectionPool.java:242)	at com.mongodb.internal.connection.DefaultConnectionPool$Task.doComplete(DefaultConnectionPool.java:1433)	at com.mongodb.internal.connection.DefaultConnectionPool$Task.execute(DefaultConnectionPool.java:1419)	at com.mongodb.internal.connection.DefaultConnectionPool$AsyncWorkManager.lambda$workerRun$4(DefaultConnectionPool.java:1356)	at com.mongodb.internal.time.Timeout.lambda$run$9(Timeout.java:200)	at com.mongodb.internal.time.TimePoint.checkedCall(TimePoint.java:101)	at com.mongodb.internal.time.Timeout.call(Timeout.java:176)	at com.mongodb.internal.time.Timeout.run(Timeout.java:196)	at com.mongodb.internal.connection.DefaultConnectionPool$AsyncWorkManager.workerRun(DefaultConnectionPool.java:1354)	at com.mongodb.internal.connection.DefaultConnectionPool$AsyncWorkManager.runAndLogUncaught(DefaultConnectionPool.java:1383)	at com.mongodb.internal.connection.DefaultConnectionPool$AsyncWorkManager.lambda$initUnlessClosed$1(DefaultConnectionPool.java:1324)	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:317)	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)	at java.base/java.lang.Thread.run(Thread.java:1583) 

       

      I think it can't write discrimator field because value classes serialized as literal. But my polymorphic structure has default deserializer, so discriminator is not really needed. So i propose as solution: If polymorphic structure has default deserializer, type discriminator is optional

       

            Assignee:
            Ross Lawley
            Reporter:
            Stepan Borzistyi
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: