```@OptIn(InternalSerializationApi::class) @Experi...
# serialization
h
Copy code
@OptIn(InternalSerializationApi::class)
@ExperimentalSerializationApi
fun <T : Any> encodeDataToString(
    data: T,
) : String {
    val serializer: KSerializer<out T> = data::class.serializer() 
    return Json.encodeToString(serializer, data)
}
why is the
data::class.serializer
an out variance of the type,
Json.encodeToString(serializer, data)
screams of an error expecting data to be
CapturedType(out T)
the only way to fix this error is by casting it to a non outer variance
Copy code
val serializer = data::class.serializer() as KSerializer<T>
i’ve tried inline and reified, still same issue, only casting has fixed it, am I missing something?
p
isn't that method just the StringFormat.encodeToString helper?
the main difference being the use of the reified
serializer<T>
helper to obtain the serializer
h
if i use it as
Copy code
val serializer = serializer<T>()
val result = Json.encodeToString(serializer, data)
then it crashes, of course the function is inlined and type is reified
Copy code
kotlinx.serialization.SerializationException: Serializer for class 'kotlin.Any' is not found.
                                                                                                    Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied
p
the type being reified won't gain you any further type information at compile-time if you're using a type-argument of
Any
1
at the call-site of
encodeDataToString
the parameter is likely inferred as
Any
(or explicitly typed as
Any
)
h
I don’t know what type it’s gonna be passed down because this function is called from a star projection
DataEvent<*> -> encodeDataToString(event.value)
p
Ideally you would have a restriction on what types your
DataEvent
can hold using a sealed hierarchy - in which case you can use the root of that sealed hierarchy as the type argument. Alternatively, if you know the types which will need serializing, you can create a
SerializersModule
with the required types provided, and use a contextual lookup (assuming no type-arguments are required as you won't know them from the
DataEvent<*>
projection)
There may be alternative options using an explicitly-defined polymorphic type in the serializers module as well - best to read up: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md
h
I can’t give restriction to my `DataEvent`since this will be a library and I don’t know the type beforehand, anything can be sent, the code works with
val serializer = data::class.serializer() as KSerializer<T>
was just wondering why I need explicit casting from
<out T>
to
<T>
p
because
::class
returns
KClass<out T>
as that's the variance that can be guaranteed. I strongly suggest against using
KClass<T>.serializer()
though as (from the docs):
This method uses platform-specific reflection available for the given erased KClass and it is not recommended to use this method for anything, but last-ditch resort, e. g. when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
It may work for some simple test cases for your library, but it won't for more complex types.
h
as I can see Navigation for compose type safety with serialization does this same thing as I do above and fits the same use case, so i’m gonna stick with the approach that works cause i wasn’t able to fix it with a
serializer<T>
because the data can come from any module, it can be any type, as long as it’s serializable i can process it
Copy code
// Finds destination within _graph including its children and
// generates a route filled with args based on the serializable object.
// Throws if destination with `route` is not found
@OptIn(InternalSerializationApi::class)
private fun <T : Any> generateRouteFilled(route: T): String {
    val id = route::class.serializer().generateHashCode()
    val destination = graph.findDestinationComprehensive(id, true)
    // throw immediately if destination is not found within the graph
    requireNotNull(destination) {
        "Destination with route ${route::class.simpleName} cannot be found " +
            "in navigation graph $_graph"
    }
    return generateRouteWithArgs(
        route,
        // get argument typeMap
        destination.arguments.mapValues { it.value.type }
    )
}