How to use #Serializable as a interface? - kotlin

I would like to write an function, that would work on any object which is serializable. Something like this:
inline fun <reified #Serializable T> T.serialiseToJson(): String {
return format.encodeToString(this)
}
This doesnt work because you cant use #Serializable to annotate type parameter.
Is there a way to do this?

A common way to handle this is to take a serializer as a parameter.
fun <T> T.serializeToJson(serializer: KSerializer<T>): String {
return format.encodeToString(serializer, this)
}
It's a bit more verbose, but it's also more flexible because it allows the user to pass a custom serializer instead of always picking the generated one. And you have the same type-safety because you can only use it with types for which a KSerializer exists.

Related

Kotlinx serialisation, common interface or type class

I am working on a plugin type system where 3rd parties will register classes that will expose data. I don't know exactly what the data will look like but I will enumerate these plugin instances collect the data and I would like to serialise it. A simplified version.
interface DataProvider {
fun getDataThatIsSerializable() : ???
}
What can i set the return type to so that I know that I will be able to serialise it with kotlinx serialisation. I cannot see any common interface that is injected into the class and given thet kotlin doesn't support type classes its not clear how to achieve what I am trying to do?
I considered something like this:
interface DataProvider {
fun getDataThatIsSerializable() : Pair<Any,KSerializer<*>>
}
but i could not pass this into the Json.encodeAsString functions, the types do not match
Are there any other options I can consider?
kotlinx.serialization doesn't like serializing things unless you can tell it exactly what you're working with.
Would it make sense for the DataProvider to be responsible for serializing its own data? Something like:
interface DataProvider {
fun getDataThatIsSerializable() : Any
fun encodeAsJsonString(data: Any) : String
}
#Serializable
data class Foo(val value: Int)
class FooDataProvider : DataProvider {
override fun getDataThatIsSerializable() : Any {
return Foo(7)
}
override fun encodeAsJsonString(data: Any): String {
return Json.encodeToString(Foo.serializer(), data as Foo)
}
}

Need to serialize Any to base64 string in Kotlin

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 ""
}
}
}
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)
kotlinx.serialization is a compile-time library, so you must know up front every object you want to serialise.
Depending on your use case;
You would either need to use a runtime library that uses reflection (such as one of the many JSON, or XML serialisers) to be able to use classes that are not your own.
Or if your requirement is more that you want to be able to serialise other peoples classes then you can use your own interface that others must implement along with #Serializable (see https://github.com/Kotlin/kotlinx.serialization/issues/1005).

How to make a generic function for enumValues<T> in Kotlin?

I struggle with providing a type as parameter for a procedure that uses the enumValues<MyEnum>() function.
Got it to work with reified but using inline functions all the way is no option for me.
fun <T: Enum<Trait>> traits(
selectionState: SnapshotStateMap<Trait, Boolean>
) {
val chunks = enumValues<T>().toList().chunked(5)
chunks.forEach {
Row {
it.forEach {
TraitIcon(it, selectionState)
}
}
}
}
My enums all derive from enum class Trait. So in fact I want to pass enum class TraitFoo: Trait, enum class TraitBar: Trait and so on into the function.
Cannot use 'T' as reified type parameter. Use a class instead.
Is the compile error I receive here. Any idea of solving this? I am somewhat confused why this is not working.
Looking at the implementation of enumValues:
public inline fun <reified T : Enum<T>> enumValues(): Array<T>
I see it uses reified. That does mean the type has to be known at compile time. Therefore I can not pass a generic but need to pass an explicit type? Is that the issue?
If yes - is there another way to achieve this rather than using reified ?
If you want to be able to use T in your function as if it's a real type then it must be reified. And in order for a type parameter to be reified it must be part of an inline function. So you're going to need an inline function.
The next bit is figuring out the generics. You currently have:
<T : Enum<Trait>>
That means, due to the nature of enums, that T can't possibly be anything other than Trait. However, you have since clarified that Trait is not an enum but is actually an interface that's implemented by various enum classes. So what you really want is T to be bounded by both Enum<T> and Trait.
Given all this, I believe what you're looking for is the following:
inline fun <reified T> traits(
selectionState: SnapshotTraitMap<Trait, Boolean>
) where T : Enum<T>, T : Trait {
val chunks = enumValues<T>().toList().chunked(5)
chunks.forEach {
Row {
it.forEach {
TraitIcon(it, selectionState)
}
}
}
}

How to pass Generic Interface T to function with class reference as parameter

I have a Generic Interface for retrofit2 http request
interface BaseGetRequest<T: Any> {
#GET fun requestGet(#Url urlGetRequest: String,#FieldMap parameters: Map<String, String>): Single<T>
}
and now I need to pass the BaseGetRequest to retrofit2 as class reference, in java this is just treated as BaseGetRequest.java, but does not allow in kotlin because it says type mismatch.
Create
How do I solve this?
Basically you should return type that you want to get as response.
If you do not need result I would recommend to use Completable instead of Single.
But if you need single guess you can write just:
NetworkUtils.builder().create(BaseRequest::class.java)
without any diamond operator

why there is 'by' for the extended class and reified in function define

coming across a sample with a class and a function and trying to understand the koltin syntax there,
what does this IMeta by dataItem do? looked at https://kotlinlang.org/docs/reference/classes.html#classes and dont see how to use by in the derived class
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
class DerivedStreamItem(private val dataItem: IMeta, private val dataType: String?) :
IMeta by dataItem {
override fun getType(): String = dataType ?: dataItem.getType()
fun getData(): DerivedData? = getDataItem()
private inline fun <reified T> getDataItem(): T? = if (dataItem is T) dataItem else null
}
for the reference, copied the related defines here:
interface IMeta {
fun getType() : String
fun getUUIDId() : String
fun getDataId(): String?
}
class DerivedData : IMeta {
override fun getType(): String {
return "" // stub
}
override fun getUUIDId(): String {
return "" // stub
}
override fun getDataId(): String? {
return "" // stub
}
}
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
There is some good documentation on reified type parameters, but I'll try to boil it down a bit.
The reified keyword in Kotlin is used to get around the fact that the JVM uses type erasure for generic. That means at runtime whenever you refer to a generic type, the JVM has no idea what the actual type is. It is a compile-time thing only. So that T in your example... the JVM has no idea what it means (without reification, which I'll explain).
You'll notice in your example that you are also using the inline keyword. That tells Kotlin that rather than call a function when you reference it, to just insert the body of the function inline. This can be more efficient in certain situations. So, if Kotlin is already going to be copying the body of our function at compile time, why not just copy the class that T represents as well? This is where reified is used. This tells Kotlin to refer to the actual concrete type of T, and only works with inline functions.
If you were to remove the reified keyword from your example, you would get an error: "Cannot check for instance of erased type: T". By reifying this, Kotlin knows what actual type T is, letting us do this comparison (and the resulting smart cast) safely.
(Since you are asking two questions, I'm going to answer them separately)
The by keyword in Kolin is used for delegation. There are two kinds of delegation:
1) Implementation by Delegation (sometimes called Class Delegation)
This allows you to implement an interface and delegate calls to that interface to a concrete object. This is helpful if you want to extend an interface but not implement every single part of it. For example, we can extend List by delegating to it, and allowing our caller to give us an implementation of List
class ExtendedList(someList: List) : List by someList {
// Override anything from List that you need
// All other calls that would resolve to the List interface are
// delegated to someList
}
2) Property Delegation
This allows you to do similar work, but with properties. My favorite example is lazy, which lets you lazily define a property. Nothing is created until you reference the property, and the result is cached for quicker access in the future.
From the Kotlin documentation:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}