kmongo icon indicating copy to clipboard operation
kmongo copied to clipboard

kotlinx.serialization: Remove the need of @Contextual annotation on Id<T> property

Open NathanPB opened this issue 4 years ago • 8 comments

I'm sorry if this is a dupe, couldn't find anything about that in all the web (including docs and this repository). I guess this issue is with kMongo judging by the message of the exception.

I'm having this issue on collection.insertOne calls. My dependencies looks like this:

dependencies {
    implementation(kotlin("stdlib"))
    implementation("io.ktor:ktor-server-netty:1.4.1")
    implementation("io.ktor:ktor-serialization:1.4.1")
    implementation("com.google.firebase:firebase-admin:7.0.0")
    implementation("com.github.NathanPB:BootingBits:1.0-SNAPSHOT")
    implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.1.2")
    implementation("org.slf4j:slf4j-simple:1.7.30")
}

and the class of the entity I'm trying to insert looks like this:

@Serializable
data class Tag (
    @SerialName("_id") @MongoId val id: Id<Tag>? = newId(),
    val displayName: String,
    val description: String? = null,
    val createdAt: Long = System.currentTimeMillis()
) {

    fun validate(): Boolean {
        return displayName.isNotEmpty()
    }

}

this is happening in any call like that:

runBlocking {
    collection.insertOne(Tag(displayName = "foobar"))
}

and generates the following (pretty long) stacktrace: (dev.nathanpb.wmd classes are from my application)

Exception in thread "main" kotlinx.serialization.SerializationException: Class 'WrappedObjectId' is not registered for polymorphic serialization in the scope of 'Id'.
Mark the base class as 'sealed' or register the serializer explicitly.
	at kotlinx.serialization.internal.AbstractPolymorphicSerializerKt.throwSubtypeNotRegistered(AbstractPolymorphicSerializer.kt:103)
	at kotlinx.serialization.internal.AbstractPolymorphicSerializerKt.throwSubtypeNotRegistered(AbstractPolymorphicSerializer.kt:113)
	at kotlinx.serialization.PolymorphicSerializerKt.findPolymorphicSerializer(PolymorphicSerializer.kt:96)
	at kotlinx.serialization.internal.AbstractPolymorphicSerializer.serialize(AbstractPolymorphicSerializer.kt:32)
	at kotlinx.serialization.encoding.Encoder$DefaultImpls.encodeSerializableValue(Encoding.kt:281)
	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableValue(AbstractEncoder.kt:18)
	at kotlinx.serialization.encoding.Encoder$DefaultImpls.encodeNullableSerializableValue(Encoding.kt:301)
	at kotlinx.serialization.encoding.AbstractEncoder.encodeNullableSerializableValue(AbstractEncoder.kt:18)
	at com.github.jershell.kbson.BsonEncoder.encodeNullableSerializableValue(BsonEncoder.kt:82)
	at kotlinx.serialization.encoding.AbstractEncoder.encodeNullableSerializableElement(AbstractEncoder.kt:92)
	at dev.nathanpb.wmd.data.Tag.write$Self(Tag.kt:31)
	at dev.nathanpb.wmd.data.Tag$$serializer.serialize(Tag.kt)
	at dev.nathanpb.wmd.data.Tag$$serializer.serialize(Tag.kt:30)
	at kotlinx.serialization.encoding.Encoder$DefaultImpls.encodeSerializableValue(Encoding.kt:281)
	at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableValue(AbstractEncoder.kt:18)
	at org.litote.kmongo.serialization.SerializationCodec.encode(SerializationCodec.kt:63)
	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.encode(SplittablePayload.java:200)
	at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.encode(SplittablePayload.java:187)
	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
	at com.mongodb.internal.connection.BsonWriterHelper.writeDocument(BsonWriterHelper.java:77)
	at com.mongodb.internal.connection.BsonWriterHelper.writePayload(BsonWriterHelper.java:59)
	at com.mongodb.internal.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:162)
	at com.mongodb.internal.connection.RequestMessage.encode(RequestMessage.java:138)
	at com.mongodb.internal.connection.CommandMessage.encode(CommandMessage.java:59)
	at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveAsync(InternalStreamConnection.java:393)
	at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceiveAsync(UsageTrackingInternalConnection.java:145)
	at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceiveAsync(DefaultConnectionPool.java:527)
	at com.mongodb.internal.connection.CommandProtocolImpl.executeAsync(CommandProtocolImpl.java:77)
	at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.executeAsync(DefaultServer.java:273)
	at com.mongodb.internal.connection.DefaultServerConnection.executeProtocolAsync(DefaultServerConnection.java:218)
	at com.mongodb.internal.connection.DefaultServerConnection.commandAsync(DefaultServerConnection.java:135)
	at com.mongodb.internal.operation.MixedBulkWriteOperation.executeCommandAsync(MixedBulkWriteOperation.java:440)
	at com.mongodb.internal.operation.MixedBulkWriteOperation.executeBatchesAsync(MixedBulkWriteOperation.java:348)
	at com.mongodb.internal.operation.MixedBulkWriteOperation.access$1000(MixedBulkWriteOperation.java:76)
	at com.mongodb.internal.operation.MixedBulkWriteOperation$2$1.call(MixedBulkWriteOperation.java:228)
	at com.mongodb.internal.operation.OperationHelper.validateWriteRequests(OperationHelper.java:269)
	at com.mongodb.internal.operation.MixedBulkWriteOperation$2.call(MixedBulkWriteOperation.java:211)
	at com.mongodb.internal.operation.OperationHelper$9.onResult(OperationHelper.java:733)
	at com.mongodb.internal.operation.OperationHelper$9.onResult(OperationHelper.java:730)
	at com.mongodb.internal.connection.DefaultServer$1.onResult(DefaultServer.java:116)
	at com.mongodb.internal.connection.DefaultServer$1.onResult(DefaultServer.java:105)
	at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:48)
	at com.mongodb.internal.connection.DefaultConnectionPool$2.onResult(DefaultConnectionPool.java:229)
	at com.mongodb.internal.connection.DefaultConnectionPool$2.onResult(DefaultConnectionPool.java:210)
	at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:447)
	at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:438)
	at com.mongodb.internal.connection.UsageTrackingInternalConnection$1.onResult(UsageTrackingInternalConnection.java:66)
	at com.mongodb.internal.connection.UsageTrackingInternalConnection$1.onResult(UsageTrackingInternalConnection.java:58)
	at com.mongodb.internal.connection.InternalStreamConnection$1$1.onResult(InternalStreamConnection.java:189)
	at com.mongodb.internal.connection.InternalStreamConnection$1$1.onResult(InternalStreamConnection.java:175)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer.completeConnectionDescriptionInitializationAsync(InternalStreamConnectionInitializer.java:215)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer.access$100(InternalStreamConnectionInitializer.java:44)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer$1.onResult(InternalStreamConnectionInitializer.java:83)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer$1.onResult(InternalStreamConnectionInitializer.java:76)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer$2.onResult(InternalStreamConnectionInitializer.java:195)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer$2.onResult(InternalStreamConnectionInitializer.java:176)
	at com.mongodb.internal.connection.CommandHelper$1.onResult(CommandHelper.java:59)
	at com.mongodb.internal.connection.CommandHelper$1.onResult(CommandHelper.java:53)
	at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:463)
	at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:440)
	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:745)
	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:712)
	at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:582)
	at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:579)
	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:250)
	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:233)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(UnixAsynchronousSocketChannelImpl.java:568)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:276)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:297)
	at com.mongodb.internal.connection.AsynchronousSocketChannelStream$AsynchronousSocketChannelAdapter.read(AsynchronousSocketChannelStream.java:144)
	at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:118)
	at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:107)
	at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:579)
	at com.mongodb.internal.connection.InternalStreamConnection.access$1100(InternalStreamConnection.java:78)
	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:702)
	at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:687)
	at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:582)
	at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:579)
	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:250)
	at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:233)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(UnixAsynchronousSocketChannelImpl.java:568)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:276)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:297)
	at com.mongodb.internal.connection.AsynchronousSocketChannelStream$AsynchronousSocketChannelAdapter.read(AsynchronousSocketChannelStream.java:144)
	at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:118)
	at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:107)
	at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:579)
	at com.mongodb.internal.connection.InternalStreamConnection.access$1100(InternalStreamConnection.java:78)
	at com.mongodb.internal.connection.InternalStreamConnection$2.onResult(InternalStreamConnection.java:440)
	at com.mongodb.internal.connection.InternalStreamConnection$2.onResult(InternalStreamConnection.java:429)
	at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:48)
	at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:536)
	at com.mongodb.internal.connection.InternalStreamConnection$3.completed(InternalStreamConnection.java:533)
	at com.mongodb.internal.connection.AsynchronousChannelStream$1.completed(AsynchronousChannelStream.java:94)
	at com.mongodb.internal.connection.AsynchronousChannelStream$1.completed(AsynchronousChannelStream.java:88)
	at com.mongodb.internal.connection.AsynchronousChannelStream$2.completed(AsynchronousChannelStream.java:197)
	at com.mongodb.internal.connection.AsynchronousChannelStream$2.completed(AsynchronousChannelStream.java:191)
	at com.mongodb.internal.connection.AsynchronousChannelStream$AsyncWritableByteChannelAdapter$WriteCompletionHandler.completed(AsynchronousChannelStream.java:222)
	at com.mongodb.internal.connection.AsynchronousChannelStream$AsyncWritableByteChannelAdapter$WriteCompletionHandler.completed(AsynchronousChannelStream.java:213)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implWrite(UnixAsynchronousSocketChannelImpl.java:752)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.write(AsynchronousSocketChannelImpl.java:382)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.write(AsynchronousSocketChannelImpl.java:399)
	at java.base/java.nio.channels.AsynchronousSocketChannel.write(AsynchronousSocketChannel.java:582)
	at com.mongodb.internal.connection.AsynchronousSocketChannelStream$AsynchronousSocketChannelAdapter.write(AsynchronousSocketChannelStream.java:177)
	at com.mongodb.internal.connection.AsynchronousChannelStream$AsyncWritableByteChannelAdapter.write(AsynchronousChannelStream.java:210)
	at com.mongodb.internal.connection.AsynchronousChannelStream.pipeOneBuffer(AsynchronousChannelStream.java:191)
	at com.mongodb.internal.connection.AsynchronousChannelStream.writeAsync(AsynchronousChannelStream.java:88)
	at com.mongodb.internal.connection.InternalStreamConnection.writeAsync(InternalStreamConnection.java:533)
	at com.mongodb.internal.connection.InternalStreamConnection.sendMessageAsync(InternalStreamConnection.java:529)
	at com.mongodb.internal.connection.InternalStreamConnection.sendCommandMessageAsync(InternalStreamConnection.java:429)
	at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveAsync(InternalStreamConnection.java:398)
	at com.mongodb.internal.connection.CommandHelper.executeCommandAsync(CommandHelper.java:52)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer.initializeConnectionDescriptionAsync(InternalStreamConnectionInitializer.java:175)
	at com.mongodb.internal.connection.InternalStreamConnectionInitializer.initializeAsync(InternalStreamConnectionInitializer.java:70)
	at com.mongodb.internal.connection.InternalStreamConnection$1.completed(InternalStreamConnection.java:174)
	at com.mongodb.internal.connection.InternalStreamConnection$1.completed(InternalStreamConnection.java:171)
	at com.mongodb.internal.connection.AsynchronousSocketChannelStream$OpenCompletionHandler.completed(AsynchronousSocketChannelStream.java:115)
	at com.mongodb.internal.connection.AsynchronousSocketChannelStream$OpenCompletionHandler.completed(AsynchronousSocketChannelStream.java:100)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:285)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:200)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:215)
	at java.base/sun.nio.ch.EPollPort$EventHandlerTask.run(EPollPort.java:306)
	at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at java.base/java.lang.Thread.run(Thread.java:832)

NathanPB avatar Oct 09 '20 00:10 NathanPB

@NathanPB do you follow this setup https://litote.org/kmongo/quick-start/#asynchronously-with-coroutines ?

Looks like you don't have the KMongo module registered - be sure to use a mongo client coming from KMongo.createClient().coroutine

zigzago avatar Oct 11 '20 14:10 zigzago

@zigzago yes, i followed these steps, I am using KMongo.createClient("String"). coroutine

NathanPB avatar Oct 11 '20 15:10 NathanPB

Well in the end this is "normal". You just have to add a @Contextual annotation on the id property. I know this is not ideal but it is a current documented limitation of the implementation. I'm going to change the title of the issue then ;). See https://github.com/Litote/kmongo/commit/eea20053801fd737fa75876a5e070114bb60e78a for the unit test

zigzago avatar Oct 11 '20 19:10 zigzago

Summary: now that the polymorphism strategy of koltinx.serialization seems to be stabilized, we need to register a dedicated polymorphism module forId<T>in order to avoid the need of @Contextual annotation

zigzago avatar Oct 11 '20 19:10 zigzago

Ok, I'll do that. Thanks for fast response

NathanPB avatar Oct 12 '20 10:10 NathanPB

@zigzago sorry to come to this again, I tried the solution you suggested, it worked for kMongo functions (insertOne, find, etc), but it fails completely when used with kTor (ex. context.respond(collection.find().toList())). Exactly the same exception

I looked at #218 that could be heavily related but it seems that it was fixed long ago, and I'm using the latest version available

NathanPB avatar Oct 13 '20 09:10 NathanPB

Look at the doc - it should work: https://litote.org/kmongo/object-mapping/#id-json-kotlinx-serialization

zigzago avatar Oct 13 '20 18:10 zigzago

@NathanPB I have the KMongo, Ktor, Kotlinx.serialization and Kotlinx.Coroutines stack as well.

Do you have the implementation("org.litote.kmongo:kmongo-id-serialization:4.1.3") dependency?

and do you have the module registered:

install(ContentNegotiation) {
        json(
            Json { serializersModule = IdKotlinXSerializationModule  },
            contentType = ContentType.Application.Json
        )

    }

Moortu avatar Oct 19 '20 09:10 Moortu