Kotlin中BufferedImage列表序列化报错:找不到对应序列化器
我正在实现一个协议,需要通过Socket发送一组小图片,采用JSON格式并将图片Base64编码。编写了如下数据类:
@Serializable sealed class CmdBase { abstract val cmd: Command } @Serializable @SerialName("CmdIdImgs") class CmdIdImgs( override val cmd: Command, val id: String, @Serializable(with = ImageListSerializer::class) val thumbnails: List<BufferedImage>) : CmdBase()
随后为BufferedImage添加序列化器:
object ImageSerializer: KSerializer<BufferedImage> { override val descriptor = PrimitiveSerialDescriptor("Image.image", PrimitiveKind.STRING) override fun deserialize(decoder: Decoder): BufferedImage { val b64str = decoder.decodeString() return ImageIO.read(ByteArrayInputStream(Base64.getDecoder().decode(b64str))) } override fun serialize(encoder: Encoder, value: BufferedImage) { val buff = ByteArrayOutputStream() ImageIO.write(value, "PNG", buff) val b64str = Base64.getEncoder().encodeToString(buff.toByteArray()) encoder.encodeString(b64str) } }
由于是BufferedImage列表,又添加了列表序列化器:
class ImageListSerializer: KSerializer<List<BufferedImage>> { private val listSerializer = ListSerializer(ImageSerializer) override val descriptor: SerialDescriptor = listSerializer.descriptor override fun serialize(encoder: Encoder, value: List<BufferedImage>) { listSerializer.serialize(encoder, value) } override fun deserialize(decoder: Decoder): List<BufferedImage> = with(decoder as JsonDecoder) { decodeJsonElement().jsonArray.mapNotNull { try { json.decodeFromJsonElement(ImageSerializer, it) } catch (e: SerializationException) { e.printStackTrace() null } } } }
最后为CmdIdImgs类添加序列化器:
object CmdIdImgsSerializer : SerializationStrategy<CmdIdImgs>, DeserializationStrategy<CmdIdImgs> { override val descriptor = buildClassSerialDescriptor("CmdIdImgs") { element("cmd", Command.serializer().descriptor) element("id", String.serializer().descriptor) element("thumbnails", ImageListSerializer().descriptor) } override fun serialize(encoder: Encoder, value: CmdIdImgs) { encoder.encodeStructure(descriptor) { encodeSerializableElement(descriptor, 0, Command.serializer(), value.cmd) encodeSerializableElement(descriptor, 1, String.serializer(), value.id) encodeSerializableElement(descriptor, 2, ImageListSerializer(), value.thumbnails) } } override fun deserialize(decoder: Decoder): CmdIdImgs = decoder.decodeStructure(descriptor) { var cmd: Command = Command.FULL_TREE var id: String = "" var thumbnails: List<BufferedImage> = listOf() loop@ while (true) { when (val i = decodeElementIndex(descriptor)) { 0 -> cmd = decodeSerializableElement(descriptor, i, Command.serializer()) 1 -> id = decodeSerializableElement(descriptor, i, String.serializer()) 2 -> thumbnails = decodeSerializableElement(descriptor, i, ImageListSerializer()) CompositeDecoder.DECODE_DONE -> break else -> throw SerializationException("Unknown index $i") } } CmdIdImgs(cmd, id, thumbnails) } }
但运行时仍报错:
Serializer has not been found for type 'BufferedImage'
报错指向CmdIdImgs类中的val thumbnails: List<BufferedImage>字段。作为Kotlin新手,请问我哪里出错了?
核心问题
你同时做了两件冲突的操作:
- 给
CmdIdImgs类手动编写了自定义序列化器CmdIdImgsSerializer - 又给
CmdIdImgs类加上了@Serializable注解
Kotlin Serialization的规则是:当类同时存在@Serializable注解和手动序列化器时,会优先尝试用注解生成默认序列化逻辑,但默认逻辑无法识别BufferedImage这种自定义类型,所以抛出找不到序列化器的错误。另外,你给thumbnails字段标注的@Serializable(with = ImageListSerializer::class)在使用手动序列化器时不会生效,因为手动序列化器会接管整个类的序列化流程。
修复步骤
方案一:移除手动编写的CmdIdImgsSerializer(推荐)
既然已经给字段指定了自定义序列化器,完全不需要手动编写整个类的序列化器,框架会自动处理:
- 删除
CmdIdImgsSerializer的全部代码 - 保留
CmdIdImgs类上的@Serializable和@SerialName("CmdIdImgs")注解 - 保留
thumbnails字段的@Serializable(with = ImageListSerializer::class)注解
修改后的CmdIdImgs类:
@Serializable @SerialName("CmdIdImgs") class CmdIdImgs( override val cmd: Command, val id: String, @Serializable(with = ImageListSerializer::class) val thumbnails: List<BufferedImage>) : CmdBase()
方案二:保留手动序列化器,移除类的@Serializable注解
如果一定要用手动编写的序列化器,需要:
- 去掉
CmdIdImgs类上的@Serializable注解 - 在序列化/反序列化时显式指定
CmdIdImgsSerializer:
// 序列化示例 val jsonString = Json.encodeToString(CmdIdImgsSerializer, yourCmdIdImgsInstance) // 反序列化示例 val cmdIdImgs = Json.decodeFromString(CmdIdImgsSerializer, jsonString)
额外优化建议
你的ImageListSerializer其实没必要手动编写,直接用ListSerializer(ImageSerializer)即可简化代码:
- 删除
ImageListSerializer类 - 将
thumbnails字段的注解改为:
@Serializable(with = ListSerializer::class) val thumbnails: List<@Serializable(with = ImageSerializer::class) BufferedImage>
或者更简洁的方式:提前给BufferedImage注册全局默认序列化器,创建Json实例时配置:
val json = Json { serializersModule = SerializersModule { contextual(BufferedImage::class, ImageSerializer) } }
这样List<BufferedImage>就不需要额外指定序列化器,直接使用即可。
内容的提问来源于stack exchange,提问作者Kingfranz

