Litote / kmongo

[deprecated] KMongo - a Kotlin toolkit for Mongo
https://litote.org/kmongo/
Apache License 2.0
782 stars 75 forks source link

Kotlinx Serialization? #419

Open cybellereaper opened 1 year ago

cybellereaper commented 1 year ago

How does one achieve serialization with kotlinx instead of fasterxml with generics?

I haven't found a solution to this and mongodb seems to be popping out errors often due to "serialization" issues? I'm wondering if there's any more documentation, because the current documentation isn't that straight forward on how to change the serialization on Kmongo?

miguelalexteixeira commented 1 year ago

Can you provide more detail about what you're trying to do? What's the structure of the data you're trying to serialize?

cybellereaper commented 1 year ago

Parsing from generics?

open class MongoDBStorage<T : Any>(clazz: Class<T>, databaseName: String, collectionName: String) : Storage<T> {
    private val client = KMongo.createClient(
        MongoClientSettings.builder()
            .uuidRepresentation(UuidRepresentation.STANDARD)
            .build()
    )

    private val database = client.getDatabase(databaseName)
    private val collection = database.getCollection(collectionName, clazz)

    override fun add(id: Id<T>, entity: T) {
        // Perform the update operation using MongoDB driver
        collection.updateOneById(id, entity, UpdateOptions().upsert(true))
    }

    override fun get(id: Id<T>): T? {
        return collection.findOneById(id)
    }

    override fun getAll(): List<T> {
        return collection.find().toList()
    }

    override fun remove(id: Id<T>) {
        collection.deleteOneById(id)
    }
}
miguelalexteixeira commented 1 year ago

Sorry it took me awhile to respond. This works for me (using org.litote.kmongo:kmongo-coroutine-serialization:4.9.0)

Entity:

@Serializable
public data class UserEntity(
    @Contextual
    @SerialName("_id")
    val id: Id<UserEntity> = newId(),

    @SerialName("first_name")
    val firstName: String,

    @SerialName("last_name")
    val lastName: String
)

Repository:

public interface GenericRepository<T> {
    public suspend fun add(id: Id<T>, entity: T)

    public suspend fun get(id: Id<T>): T?
}

internal class MongoGenericRepository<T: Any>(
    private val kMongoDatabase: CoroutineDatabase,
    private val collectionName: String,
    private val clazz: Class<T>
) : GenericRepository<T> {

    private val collection: CoroutineCollection<T> by lazy {
        kMongoDatabase.database.getCollection(collectionName, clazz).coroutine
    }

    override suspend fun get(id: Id<T>): T? {
        return collection.findOneById(id)
    }

    override suspend fun add(id: Id<T>, entity: T) {
        collection.updateOneById(id, entity, UpdateOptions().upsert(true))
    }
}

I'm assuming that you'll only need to (de)serialize the same entity for a given collection, i.e. only UserEntity instances in the collection users.

miguelalexteixeira commented 1 year ago

If you need to store different entities under the same collection, then you'll need to include a type discriminator in the resulting BSON, so that kotlinx-serialization knows how to deserialize it. This can be achieved through either closed or open polymorphism.

In case you go with open polymorphism, you can call registerModule(serializersModule) to register your custom SerializersModule with KMongo.

You can also further configure the serialization through the global configuration property. E.g.:

configuration = configuration.copy(
    encodeDefaults = false,
    classDiscriminator = SchemaRegistry.TYPE_DISCRIMINATOR
)