Home > other >  Need to serialize Any to base64 string in Kotlin
Need to serialize Any to base64 string in Kotlin

Time:10-29

I have the following code in Kotlin which I aim to use it to convert any instance to a base64 encoded string. The same is not working and it throws the following error :

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

How can I fix this?

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 ""
    }
  }
}

CodePudding user response:

Serializing just Any is not as simple as it sounds. Serialization framework has to know the type of the data to serialize. It can use either compile type (Any in your case) or runtime type (actual type provided to toBase64()). Both options have their drawbacks. Runtime type is incomplete due to type erasure, so e.g. List<Int> and List<String> are the same. On the other hand, compile-time type may be totally lost, e.g. in generics or in cases like yours.

Kotlin serialization generally prefers compile types, especially because reified parameters make them much more usable. Unfortunately, we can't use reified here, because toBase64() is a virtual function, so it can't be inlined.

My suggestion is to change the signature of this function to additionally receive KType of provided data and then create inline function to make it convenient to use:

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>())

Alternatively, we can serialize using the runtime type, but note the problems I mentioned earlier - it may not work well with generics.

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