You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB无法找到泛型类Id的编解码器,求解决方案

问题描述

使用Kotlin泛型内联值类作为类型化标识,代码如下:

@JvmInline
@Serializable
value class Id<T>(
    @Contextual
    val value: ObjectId = ObjectId()
)

在所有领域模型中使用该类:

@Serializable
data class Test(
    override var _id: Id<Test> = Id(),
    val value: String,
) 

@Serializable
data class Status(
    override var _id: Id<Status> = Id(),
    var test_id: Id<Test>,
    val value: String
)

// ...其他领域模型

数据库初始化正常,文档存储格式正确,但从数据库读取数据时抛出错误:

org.bson.codecs.configuration.CodecConfigurationException:
Can't find a codec for CodecCacheKey{clazz=class Id, types=null}.

解决方案

1. 实现自定义BSON Codec

MongoDB无法自动识别泛型内联值类,需要为Id<T>实现自定义Codec,处理其与ObjectId的相互转换:

class IdCodec<T> : Codec<Id<T>> {
    private val objectIdCodec = ObjectIdCodec()

    override fun encode(writer: BsonWriter, value: Id<T>, encoderContext: EncoderContext) {
        objectIdCodec.encode(writer, value.value, encoderContext)
    }

    override fun getEncoderClass(): Class<Id<T>> {
        @Suppress("UNCHECKED_CAST")
        return Id::class.java as Class<Id<T>>
    }

    override fun decode(reader: BsonReader, decoderContext: DecoderContext): Id<T> {
        val objectId = objectIdCodec.decode(reader, decoderContext)
        return Id(objectId)
    }
}

2. 注册Codec到Mongo客户端

初始化MongoClient时,将自定义IdCodec注册到CodecRegistry:

val codecRegistry = CodecRegistries.fromRegistries(
    MongoClientSettings.getDefaultCodecRegistry(),
    CodecRegistries.fromCodecs(IdCodec<Any>())
)

val client = MongoClients.create(
    MongoClientSettings.builder()
        .codecRegistry(codecRegistry)
        .build()
)

3. 处理泛型擦除问题

由于Kotlin泛型擦除,MongoDB无法区分不同类型参数的Id<T>,但通常上述通用Codec已能满足需求——所有Id<T>最终存储为ObjectId,读取时会自动实例化为对应Id<T>实例。如果需要严格类型区分,可在Codec中结合类型参数做额外校验,但一般场景下无需此操作。

4. 配合Kotlinx Serialization的补充配置

若同时使用Kotlinx Serialization,需确保@Contextual注解生效,或在序列化模块中注册ObjectId的序列化器:

val module = SerializersModule {
    contextual(ObjectId::class, ObjectIdSerializer)
}

val format = Json {
    serializersModule = module
}

(注:ObjectIdSerializer可自行实现,或使用org.litote.kmongo:kmongo-serialization等第三方库提供的实现)

内容的提问来源于stack exchange,提问作者user15861750

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:57:33