Generics in Kotlin Serialization produces compile time error - android

I am having trouble compiling a Serialization data class that is also a generic. I am very new to Android and Kotlin, but am an experienced iOS/Swift developer (don't hold it against me).
I am trying to set up a generic data class wrapper around a GraphQL Response that can return me either the T generic in a data field, or an error in the error field.
import kotlinx.serialization.Serializable
#Serializable
data class GraphQLResponse<T:Serializable> (
val errors : List<ErrorResponse>? = null,
val data : Map<String, T>? = null
) {
#Serializable
data class Location(
val line: Int? = 0,
val column: Int? = 0,
val sourceName: String? = null
)
#Serializable
data class ErrorResponse(
val locations: List<Location>? = null,
val errorType: String? = null,
val message: String? = null
)
}
Compiling this I get the following error:
java.lang.AssertionError: Couldn't load KotlinClass from /Users/brett/Development/Company/android/app/build/tmp/kotlin-classes/debug/com/company/company/network/GraphQLResponse.class; it may happen because class doesn't have valid Kotlin annotations
And also a bunch of warning around reflection.
Is what I am trying to do possible with Generics and Serialization? Do I need to write my own deserialisation methods ?
Any help and advice would be appreciated.

You need to provide a custom serializer for type T.
check this guide
(BTW, in your code type T shouldn't be Serializable - T:Serializable. It's just annotation)
There is a similar question, check this out.
However, as you can see it may not work well if you use kapt in your project even you provide the serializer.
I found this comment from the serialization repository :
However, due to the mentioned kapt issue, it's impossible for now. Probably, a viable workaround can be to move models and serializers to the separate Gradle module, which is not processed by kapt.
And there are more issues about this problem :
https://github.com/Kotlin/kotlinx.serialization/issues/1148
https://github.com/Kotlin/kotlinx.serialization/issues/685
https://github.com/Kotlin/kotlinx.serialization/issues/1313

Related

Moshi - Not enough information to infer type variable T

I have an app which is mixed Java and Kotlin.
In the Kotlin code I use Moshi to convert an object to Json in a convertor for a Room database table.
I have one case that works perfectly but another one produces the error:
Not enough information to infer type variable T
This is what my code looks like:
val type: Type = Types.newParameterizedType(
MutableMap::class.java,
LayerTwoConn::class.java,
TWeFiState::class.java,
WfMeasureFileMgr::class.java,
Traffic::class.java,
ThroughputCalculator::class.java,
CellSubTechThroughput::class.java,
LongValuesAverageCalculator::class.java,
LayerTwoConn.SenselessTraffic::class.java
)
val json = Moshi.Builder().build().adapter(type).toJson(layerTwoConn)
I have included all the classes that are used in the objects.
What have I missed?
This case works perfectly:
val type: Type = Types.newParameterizedType(
MutableList::class.java,
CnrScan::class.java,
)
val jsonAdapter: JsonAdapter<List<CnrScan>> = Moshi.Builder().build().adapter(type)
val json = jsonAdapter.toJson(list)
In this object, all the internally used classes are standard Java class and not my own.
Have I missed something simple?
I don't know if this is important but the class LayerTwoConn's constructor is private.
I think you are trying to convert too many classes into one type, try to convert MutableMap class and LayerTwoConn class.
Do note that Room uses SQL architecture, so try to predict what you want your table to contain

Retrofit/Moshi request gives error - java.lang.IllegalStateException : dangling name

I'm using Retrofit to make some API calls.
Recently, I added a new endpoint
#POST("api/test/myRequest")
fun createRequest(
#retrofit2.http.Body request: MyRequest
): Single<MyResponse>
Using the following DataClass to represent the non-serialized request
data class MyRequest(
#Json(name = "myData") #field:Json(name = "myData") var myData: String,
#Json(name = "myuuid") #field:Json(name = "myuuid") var myuuid: UUID? = null
)
When I try to make the request, it fails with the error: java.lang.IllegalStateException: Dangling name: myuuid
The top item in the stacktrace is: com.squareup.moshi.JsonUtf8Writer. It seems Moshi is throwing an error when trying to serialize the request. It's not clear to me why though.
What does this error mean, and how can I fix it?
I would suggest you to use only one of the annotations, for instance you get the issue if you do not use #Json( name, but only #field
I am not sure and could cancel this post if did not help you, but please try, I am quite curious

Unexpected behavior in Kotlin Parcelize

I am having trouble in understanding the way #Parcelize working in Kotlin. According to documentations
only the primary constructor properties will be serialized.
But when I serialize and deserialize classes with empty primary contractors, it is still serializing and deserializing all the fields. For example, below class
#Parcelize
class Node(): Parcelable {
var field: String? = null
}
As primary contractor doesn't have any field, according to documentations I should have field = null always after ser/des. But whenever I run below codes
val before = Node()
before.field = "someField"
val bundle = Bundle().apply{ putParcelable("someKey", before) }
val after = bundle.getParcelable<Node>("someKey")
field is successfully serialized and deserialized and will have value of someField.
Am I missing something or did Parcelize got updated but they didn't update documentation?
By the way if I leave Node declaration as above, Android Studio gives me warning that field will not be serialized into Parcel. But it is.
You should define it in the constructor itself and it will work just fine.
#Parcelize
class Node( var field: String? = null) : Parcelable
And to use empty constructors in kotlin you can add this in app gradle file.
apply plugin: 'kotlin-noarg'
With this you can use classes with empty constructors.
I hope this helps.

Using JSON.stringify on object without serializer needs to be marked as experimental

Using kotlin plugin 1.3.10 in Android Studio,
when I try to stringify a simple class' object to JSON, it wont compile:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
#Serializable data class Data(val a: Int, val b: Int)
val data = Data(1, 2)
val x = JSON.stringify(data)
However, giving a serialiser works:
val x = JSON.stringify(Data.serializer(), data)
I can't see anybody else having this problem, any idea what the problem is? I've set up using serialisation in gradle.build.
I import with:
import kotlinx.serialization.*
import kotlinx.serialization.json.JSON
The overload of StringFormat.stringify which doesn't take in a serializer (SerializationStrategy) is still experimental. If you view its definition (e.g. ctrl+click on it in the IDE) you'll see it looks as follows:
#ImplicitReflectionSerializer
inline fun <reified T : Any> StringFormat.stringify(obj: T): String = stringify(context.getOrDefault(T::class), obj)
Where that ImplicitReflectionSerializer annotation is itself declared in that same file (SerialImplicits.kt):
#Experimental
annotation class ImplicitReflectionSerializer
So because it's still experimental, you need to do exactly as the warning says, i.e. tell the compiler to allow the use of experimental features, by adding an annotation such as #UseExperimental... where you're using it.
Note that the quick example shown on the kotlinx.serialization GitHub repo's main readme shows that you need to pass in a serializer when calling stringify.

only classes are allowed on the left hand side of a class literal

I know a lot of similar questions here in StackOverflow, but nothing solved mine.
I have a generic data class:
data class ServiceCall<out T>(val result: T?, val exception: String?, val pagination: String?, val stringResult: String?)
I am trying to use like this:
Gson().fromJson(json, ServiceCall<SurveyListModel>::class.java).result
IDE shows error: only classes are allowed on the left hand side of a class literal
How to solve this? Thanks in advance.
You can't use generics with class, as easily observable here:
List<Int>::class.java
It gives you the same error. To use the generic typ in GSON deserialization, do what is suggested here:
https://stackoverflow.com/a/5554296/8073652
EDIT:
In Kotlin it looks like this:
val type: Type = object : TypeToken<ServiceCall<SurveyListModel>>() {}.type
Gson().fromJson<ServiceCall<SurveyListModel>>(json, type).result
Here's a small proof of concept, I've written:
class Token : TypeToken<List<Int>>()
val x: List<Int> = Gson().fromJson(Gson().toJson(arrayOf(1)), Token().type)
println(x)
If someone looking for the answer in Kotlin and Jackson.
val response = ObjectMapper().convertValue(dataObject, object: TypeReference<Map<String, Any>>(){})
If you want to pass a list of the generic objects pass it like this
listOf<MyModel>()::class
the function will be like this
fun myFunction ( inputList: KClass<T>){}

Categories

Resources