首页 > 解决方案 > 需要在 Kotlin 中将 Any 序列化为 base64 字符串

问题描述

我在 Kotlin 中有以下代码,我的目标是使用它将任何实例转换为 base64 编码的字符串。同样不起作用,它会引发以下错误:

Serializer for class 'Any' is not found.\nMark the class as @Serializable or provide the serializer explicitly

我怎样才能解决这个问题?

class SerializerAdapter: SerializerPort {
  private val logger: Logger = LoggerFactory.getLogger(javaClass.simpleName)
  override fun toBase64(input: Any): String {
    try {
      val jsonString = Json.encodeToString(input)
      return Base64.getEncoder().encodeToString(jsonString.toByteArray())
    }catch (ex: Exception) {
      logger.error("[BASE64 Error] error converting json object to base64 encoded string: ${ex.stackTraceToString()}")
    }finally {
      return ""
    }
  }
}

标签: kotlinserialization

解决方案


序列化Any并不像听起来那么简单。序列化框架必须知道要序列化的数据类型。它可以使用编译类型(Any在您的情况下)或运行时类型(提供给的实际类型toBase64())。这两种选择都有其缺点。由于类型擦除,运行时类型不完整,因此例如List<Int>List<String>是相同的。另一方面,编译时类型可能会完全丢失,例如在泛型中或在您的情况下。

Kotlin 序列化通常更喜欢编译类型,特别是因为reified参数使它们更有用。可惜我们这里不能使用reified,因为toBase64()是虚函数,所以不能内联。

我的建议是更改此函数的签名以额外接收KType提供的数据,然后创建内联函数以方便使用:

override fun toBase64(input: Any, type: KType): String {
    try {
        val serializer = Json.serializersModule.serializer(type)
        val jsonString = Json.encodeToString(serializer, input)
        ...
    }
}

@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T>  SerializerPort.toBase64(input: Any) = toBase64(input, typeOf<T>())

或者,我们可以使用运行时类型进行序列化,但请注意我之前提到的问题 - 它可能不适用于泛型。

val serializer = Json.serializersModule.serializer(input::class.starProjectedType)
val jsonString = Json.encodeToString(serializer, input)

推荐阅读