Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.4k stars 621 forks source link

How To Get Serializer From Reified Generic Parameter? #214

Closed MrThreepwood closed 6 years ago

MrThreepwood commented 6 years ago

I am attempting to use this library in order to serialize things generically for all routes handled by my Api in order to contain references to serialization/deserialization. Currently with Jackson I can do this, but I don't know how to manage it with this library.

fun <reified T> deserializeData(request: String): T {
    //typetoken/serializer stuff with reified T
    JSON.parse(serializer, request)
}

The goal here being to avoid any reference to this library about how serialization/deserialization is done outside of the few functions that actually handle that.

MrThreepwood commented 6 years ago

I kept poking at it a bit and realized that serializer is an extension function added by this package, so I can simply change it to

fun <reified T: Any> deserializeData(request: String): T {
    JSON.parse(T::class.serializer(), request)
}
mochadwi commented 5 years ago

For custom serialization, you might want to use this example here

leinardi commented 4 years ago

Any solution for Kotlin Native? T::class.serializer() uses reflection and is not available on Kotlin/Native.

sandwwraith commented 4 years ago

@leinardi try to use serializer<T>()

leinardi commented 4 years ago

@sandwwraith If I use kotlinx.serialization.serializer<T>(), I get the error Cannot use 'T' as reified type parameter. Use a class instead.

sandwwraith commented 4 years ago

Maybe your T is not reified?

leinardi commented 4 years ago

Even with the reified I get the warning This declaration is experimental and its usage must be marked with '@kotlinx.serialization.ImplicitReflectionSerializer' or '@OptIn(kotlinx.serialization.ImplicitReflectionSerializer::class)' which I assume won't work on non JVM platforms, right?

/**
 * This annotation marks declaration which try to obtain serializer implicitly
 * using reflection, e.g. from KClass or instance itself.
 *
 * This approach is discouraged in general because it has several drawbacks, including:
 * - Reflection is not available on Kotlin/Native and is very limited on Kotlin/JS.
 * - Reflection won't infer correct serializers for generic classes, like collections.
 * - SerialModule may not be available, since it is bound to particular format, not serializer.
 * - Such reflection calls are usually slow.
 *
 * It's always better to specify serializer explicitly, using generated `.serializer()`
 * function on serializable class' companion.
 */
Xerosigma commented 4 years ago

Can't get this to work right either.

sandwwraith commented 4 years ago

It may work for some limited cases, so give it a try

spragucm commented 4 years ago

I kept poking at it a bit and realized that serializer is an extension function added by this package, so I can simply change it to

fun <reified T: Any> deserializeData(request: String): T {
    JSON.parse(T::class.serializer(), request)
}

This needs a little more explanation...

I'm using Android Studio and the Kotlin Serialization library.

Simple data class:

import kotlinx.serialization.Serializable

@Serializable
data class DataTest(val name: String = "YoMamma")

The extension functions:

inline fun <reified T: Any> deserialize(data: String): T {
    return Json.parse(T::class.serializer(), data)
}

inline fun <reified T: Any> T.serialize(): String {
    return Json.stringify(T::class.serializer(), this)
}

Using the functions:

val serialIn = DataTest("yoPappa").serialize()
Log.d("Serialize In: $serialIn")
val deserialize : DataTest = deserialize(serialIn) //YOU MUST SPECIFY THE THE TYPE, that's how the type gets reified
Log.d("Serialize Out: $deserialize")

If you don't specify the type, e.g. val ... : DataTest, then you get the following error:

Can't locate companion serializer for class