Kotlin's #Parcelize throws NPE on writeToParcel() - android

I have this set of data classes, all in the same file:
#Parcelize data class Response(val RecordView: RecordView):Parcelable
#Parcelize data class RecordView(val Records: List<Records>):Parcelable
#Parcelize data class Gid(val Value: String):Parcelable
#Parcelize data class Features(val Fields: List<Fields>, val FeatureName: String, val Title: String):Parcelable
#Parcelize data class Fields(val Label: String, val Value: String, val FieldName: String, val Features: List<Features>):Parcelable
#Parcelize data class Records(val Gid: Gid, val ImageIds: List<String>, val Fields: List<Fields>, val Features: List<Features>):Parcelable
I then send the Response object as an intent for the next activity:
val record = intent?.getParcelableExtra<Records>(RECORD)
but the next activity then throws this:
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.Collection.size()' on a null object reference
at entities.Features.writeToParcel(Features.kt)
at entities.Records.writeToParcel(Records.kt)
at android.os.Parcel.writeParcelable(Parcel.java:1496)
at android.os.Parcel.writeValue(Parcel.java:1402)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:724)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1157)
at android.os.Parcel.writeBundle(Parcel.java:764)
at android.content.Intent.writeToParcel(Intent.java:8687)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:3082)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1518)
at android.app.Activity.startActivityForResult(Activity.java:4225)
This same process works when I just send a more basic class (just Strings in the parameters), using the same #Parcelize.
And yes, I turned on experimental on my gradle file.
Any ideas? Thanks

I think your API should not response -> Null Object
because data intent can't write Null (write to parcel)
when u use #Parcelize
https://youtrack.jetbrains.com/oauth?state=%2Fissue%2FKT-20032

Related

RuntimeException: Exception inflating kotlin:navigation/nav_graph

We had a RuntimeException while we put a safeArgs into the navigation graph and this crash didn't give us any more guide to fix it.
In the first impression, we were following this to ensure that those classes were Parcelize or not.
Therefore, those classes were parceled correctly and everything seems right.
After many searches and investigations into our codes, I found that SafeArgs class exists in a sealed-class file.
Considering to this point that we can't allocate the parceled annotation to the sealed classes, we've decided to move our class outside of that sealed .kt file.
Although, I found the main reason for that runtime crash was this cause.
Also, I've provided the wrong and correct cases below, We hope this will be helpful to others:
We've moved the UserFavorite and its subclass, outside of the Response.kt file,
sealed class Response {
.
.
#Parcelize
data class UserFavorite(
#SerializedName("title") val title: String,
#SerializedName("itemType") val itemType: String,
#SerializedName("emptyIcon") val emptyIcon: String,
#SerializedName("_texts") val texts: UserFavoriteTexts
) : Response(), Parcelable
#Parcelize
data class UserFavoriteTexts(
#SerializedName("hintMessage") val hintMessage: String,
#SerializedName("add") val add: String,
#SerializedName("remove") val remove: String,
#SerializedName("edit") val edit: String
): Parcelable
.
.
}
into an independent file for that: UserFavorite.kt
#Parcelize
data class UserFavorite(
#SerializedName("title") val title: String,
#SerializedName("itemType") val itemType: String,
#SerializedName("emptyIcon") val emptyIcon: String,
#SerializedName("_texts") val texts: UserFavoriteTexts
) : Response(), Parcelable
#Parcelize
data class UserFavoriteTexts(
#SerializedName("hintMessage") val hintMessage: String,
#SerializedName("add") val add: String,
#SerializedName("remove") val remove: String,
#SerializedName("edit") val edit: String
): Parcelable
and, Respons.kt
sealed class Response {
.
.
.
.
}

I got Unable to invoke no-args constructor error. retrofit caused error?

I have a sealed class that contains two models.
1-DataOFEachOrder
2-List of DataOFEachOrder
sealed class OrdersHistoryDataModel
data class SingleOrderHistoryDataModel(val `data`:DataOFEachOrder , val success: Boolean): OrdersHistoryDataModel()
data class ListOrdersHistoryDataModel(val `data`:List<DataOFEachOrder> , val success: Boolean):OrdersHistoryDataModel()
DataOFEachOrder contains:
class DataOFEachOrder(
val __v: Int,
val _id: String,
val createdAt: String,
val id: String,
val restaurant: Restaurant<*>,
val restaurantName: Any,
var isexpanded:Boolean = false
)
the restaurant property is a variable could be have two schema. so I had to define it as sealed class too. now it is nested sealed class somehow.
sealed class Restaurant<T>(val value: T)
{
//model 1 is used in moreDetailFragment
data class RestaurantModel1(
val _id: String,
val id: String, val name: String, val category: String, val owner: String) : Restaurant<String>(id)
//model 2 is used in ordersStatusFragment
data class RestaurantModel2(val restaurant: String): Restaurant<String>(restaurant)
}
My API :
//getList of histories Orders- OrderStatusFragment
#POST("orders/orderlist")
#Headers("No-Authentication: false")
suspend fun getOrdersHistory(#Body status:JsonObject):Response<ListOrdersHistoryDataModel>
I just know my error comes from Restaurant sealed class or retrofit. see other Q&A , I did not get any point.
what should I do?
the message error :
java.lang.RuntimeException: Unable to invoke no-args constructor for simpleloginregister.ui.ordersStatus.TabFragments.Restaurant<?>. Registering an InstanceCreator with Gson for this type may fix this problem.

Moshi create json with dynamic inner data class

I have moshi with retrofit and I want to send dynamic inner data object to backend.
Lets say I need to patch some data on my endpoint:
#PATCH("/animals/{animalId}/attributes")
suspend fun updateAnimal(#Path("animalId") animalId: String, #Body request: MyPetsRequest)
But my AnimalAttributes inner object is dynamic.
#JsonClass(generateAdapter = true)
#Parcelize
data class MyPetsRequest(
#Json(name = "name") val name: String,
#Json(name = "attributes") val attributes: AnimalAttributes
) : Parcelable
interface AnimalAttributes : Parcelable // or sealed class possible when share same base fields
#JsonClass(generateAdapter = true)
#Parcelize
data class DogAttributes(
#Json(name = "bark") val bark: Boolean,
#Json(name = "bite") val bite: Boolean,
) : AnimalAttributes
#JsonClass(generateAdapter = true)
#Parcelize
data class CatAttributes(
#Json(name = "weight") val weight: Int,
) : AnimalAttributes
I tried create a custom adapter for Moshi, but when I use #ToJson inner object attributes data are escaped (so complete object is send as one parameter).
class AnimalAttributesAdapter {
#ToJson
fun toJson(value: AnimalAttributes): String {
val moshi = Moshi.Builder().build()
return when (value) {
is DogAttributes -> moshi.adapter(DogAttributes::class.java).toJson(value)
is CatAttributes -> moshi.adapter(CatAttributes::class.java).toJson(value)
else -> throw UnsupportedOperationException("Unknown type to serialize object to json: $value")
}
}
/**
* Not supported, this object is used only one way to update data on server.
*/
#FromJson
fun fromJson(value: String): AnimalAttributes = throw UnsupportedOperationException()
}
Moshi.Builder().add(AnimalAttributesAdapter())
Result in this case looks like:
{"name":"my pet name","attributes":"{\"bark\":\"true\", \"bite\":\"false\"}"}
When I try to use PolymorphicJsonAdapterFactory it added next parameter between attributes which is not acceptable for my use case.
PolymorphicJsonAdapterFactory.of(AnimalAttributes::class.java, "animal")
.withSubtype(DogAttributes::class.java, "dog")
.withSubtype(CatAttributes::class.java, "cat")
Result in this case added attribute animal: {"name":"my pet name","attributes":{"animal":"dog","bark":true, "bite":false}}
I don't need to deserialize object from json to data class. I only need create one way serialization to json.

How to use a simple data class in kotlin

I am new to Kotlin and learning it, I am having a simple data class
data class Country{
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
}
Errors I am facing:
]
Your data class should look like this:
data class Country(
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
)
The difference is, like mentioned in the comments: I used normal parentheses around the fields while you used curly braces
Data Class in Kotiln must have a variable/value parameter in it's constructor declaration.
Official doc states that :
To ensure consistency and meaningful behavior of the generated code,
data classes have to fulfill the following requirements:
The primary constructor needs to have at least one parameter;
All primary constructor parameters need to be marked as val or var;
Data classes cannot be abstract, open, sealed or inner;
(before 1.1) Data classes may only implement interfaces.
So, your data class should be something similar like below :
data class Foo(
val bar: Any
)
Note: In Kotlin, you can declare class constructor just by placing '()' following by class name to make it as primary constructor.
You class declaration should be something like below :
data class Country(
#SerializedName("name")
val countryName: String?,
#SerializedName("capital")
val capital: String?,
#SerializedName("flagPNG")
val flag: String?
)
Refer here for more info.

How to parcelise member variable other than constructor in data class while using #Parcelize

I am using Room and Kotlin data class. Such as,
#Entity(tableName = "Person")
#Parcelize
class Test(#ColumnInfo(name = "name") var name:String) : Parcelable{
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "ID")
var id: Long? = null
}
I can create the instance using the constructor and insert the data. I am also getting a warning "property would not be serialized into a 'parcel'". When I was trying to send the object through a bundle, the id is missing, which is expected as the warning says so. How can I add that member ID in the parcel? I am not keeping the ID in the constructor as I want them to be generated automatically.
You can find this information with the documentation:
#Parcelize requires all serialized properties to be declared in the primary constructor. Android Extensions will issue a warning on each property with a backing field declared in the class body. Also, #Parcelize can't be applied if some of the primary constructor parameters are not properties.
If your class requires more advanced serialization logic, you can write it inside a companion class:
#Parcelize
data class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
private companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
// Custom write implementation
}
override fun create(parcel: Parcel): User {
// Custom read implementation
}
}
}

Categories

Resources