How to retrieve a parcelable object stored in Firebase (Kotlin) [duplicate] - android

https://github.com/neuberfran/JThings/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFran.kt
I have this POJO above with the error mentioned in the topic. I know it is a mistake already mentioned here, but I have tried several classes (besides this one) and I have not been successful, since my model/POJO class (and Code implementation) is different from several that I saw:(Every help is welcome)
Could not deserialize object. Class does not define a no-argument
constructor. If you are using ProGuard, make sure these constructors
are not stripped (found in field 'value')
Change made to the garagem document, exchanged value for valorb, etc...

The error is very clear, your class "FireFran" doesn't have a no-argument constructor. When you try to deserialize an object from Cloud Firestore, the Android SDKs require that the class must have a default no-arg constructor and also setters that map to each database property.
In Kotlin, the data classes don't provide a default no-arg constructor. So you need somehow ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate.
So your "FireFran" might look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var value: FireFranValue? = null //Newly added
) {
//var value: FireFranValue = FireFranValue(false, 0)
companion object Factory {
fun create() :FireViewModel = FireViewModel()
var COLLECTION = "device-configs"
var DOCUMENT = "alarme"
var FIELD_userId = "userId"
}
}
Now adding the properties in the constructor, Kotlin will automatically generate a default no-arg constructor. In this way, the Firebase Android SDK will be able to use. It will also generate setters for each property. Please see that each property is var and not a val, and provides a default null value in case of "id" and "userId".
If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
In your screenshot, the "value" property is on an object of type "FireFranValue", which has only two properties, "brightness" and "on". To be able to read the data under "value", your "FireFran" class should contain a new property of type "FireFranValue". Please check above the class.
If you don't want to use automatic deserialization, you can get the value of each property individually. For example, you can get the value of the "userId" property using DocumentSnapshot's getString(String field) method:
val userId = snapshot.getString("userId")
Edit2:
Your three classes should look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue = FireFranValue(false),
var valorb: FireFranValueB = FireFranValueB(openPercent = 0)
)
data class FireFranValue(
var on: Boolean // = false
)
data class FireFranValueB(
var openPercent: Number // = 0
)
Edit3:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue? = null,
var valorb: FireFranValueB? = null
)
data class FireFranValue(
var on: Boolean? = null
)
data class FireFranValueB(
var openPercent: Number? = null
)
Now, "on" and "openPercent" will get the values from the database.
Please check the following class declaration:

You can define a no-arg constructor in kotlin like this:
data class SampleClass(
val field1: String,
val field2: String
) {
constructor(): this("", "")
}

First thing: It was not necessary to create the field (map type) called valorb. It was resolved with value.openPercent
As I have two documents (alarme and garagem) I created two POJO classes (FireFran and FireFranB)
https://github.com/neuberfran/JThingsFinal/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFranB.kt
The secret was to treat the value.on and value.openPercent fields as maps (as facts are):

Related

Convert Firestore document to list of objects

I have this Firestore document Quiz_android that looks list this:
It is a simple array with maps in it. Now I would like to bind those results to some objects in Kotlin. Therefore I have made the following:
data class QuizBody(
val questions: List<Question>
)
data class Question(
val question: String,
val answers: List<String>,
val answer: Int
)
A Quizbody is just all the questions for the quiz in a list, and in that list, I have classes of Question which should be able to store all the data from the call.
But how do I bind the result from the call to those objects?
suspend fun getQuestions(quizToGet: String) {
try {
//firestore has support for coroutines via the extra dependency we've added :)
withTimeout(5_000) {
firestore.collection("Quizzes").document(quizToGet).get()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val result = task.result
if (result.exists()) {
val myObject = result.toObject(QuizBody::class.java)
println(myObject)
}
}
}
}
} catch (e: Exception) {
throw QuizRetrievalError("Retrieving a specific quiz was unsuccessful")
}
}
I have made this but this does not work.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hva.madlevel7task2, PID: 3995
java.lang.RuntimeException: Could not deserialize object. Class com.hva.madlevel7task2.model.QuizBody does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped
Edit:
I have updated the data class:
data class QuizBody(
var questions: List<Question>? = null
)
data class Question(
var question: String? = null,
var answers: List<String>? = null,
var answer: Int? = null
)
suspend fun getQuestions(quizToGet: String) it still the same, now I get this in the console:
I/QuizViewModel: function: getListQuestions
W/Firestore: (24.1.1) [CustomClassMapper]: No setter/field for Questions found on class com.hva.madlevel7task2.model.QuizBody (fields/setters are case sensitive!)
I/System.out: QuizBody(questions=null)
The following error:
java.lang.RuntimeException: Could not deserialize object. Class com.hva.madlevel7task2.model.QuizBody does not define a no-argument constructor.
Is very straightforward in my opinion. Your class "QuizBody" does not have a no-argument constructor. When you try to deserialize an object that comes from a Firestore database, the Android SDKs require that the class should mandatorily have a default no-argument constructor.
In Kotlin, the data classes don't provide a default no-arg constructor if all the properties of the class are declared with val. For such properties, Kotlin requires that their values be specified in the constructor since they can't possibly change later. So this is required because we need to ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate. So your classes should look like this:
data class QuizBody(
var questions: List<Question>? = null
👆 👆
)
data class Question(
var question: String? = null,
var answers: List<String>? = null,
var answer: Int? = null
)
Now adding the properties in the constructor, Kotlin will automatically generate a default no-argument constructor. In this way, the Firebase Android SDK will be able to use to generate setters for each property. If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
val myObject = document.toObject(QuizBody::class.java)
println(myObject)
}
}

Could not deserialize object. Class does not define a no-argument constructor.If you are using ProGuard, make sure these constructors are not stripped

https://github.com/neuberfran/JThings/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFran.kt
I have this POJO above with the error mentioned in the topic. I know it is a mistake already mentioned here, but I have tried several classes (besides this one) and I have not been successful, since my model/POJO class (and Code implementation) is different from several that I saw:(Every help is welcome)
Could not deserialize object. Class does not define a no-argument
constructor. If you are using ProGuard, make sure these constructors
are not stripped (found in field 'value')
Change made to the garagem document, exchanged value for valorb, etc...
The error is very clear, your class "FireFran" doesn't have a no-argument constructor. When you try to deserialize an object from Cloud Firestore, the Android SDKs require that the class must have a default no-arg constructor and also setters that map to each database property.
In Kotlin, the data classes don't provide a default no-arg constructor. So you need somehow ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate.
So your "FireFran" might look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var value: FireFranValue? = null //Newly added
) {
//var value: FireFranValue = FireFranValue(false, 0)
companion object Factory {
fun create() :FireViewModel = FireViewModel()
var COLLECTION = "device-configs"
var DOCUMENT = "alarme"
var FIELD_userId = "userId"
}
}
Now adding the properties in the constructor, Kotlin will automatically generate a default no-arg constructor. In this way, the Firebase Android SDK will be able to use. It will also generate setters for each property. Please see that each property is var and not a val, and provides a default null value in case of "id" and "userId".
If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
In your screenshot, the "value" property is on an object of type "FireFranValue", which has only two properties, "brightness" and "on". To be able to read the data under "value", your "FireFran" class should contain a new property of type "FireFranValue". Please check above the class.
If you don't want to use automatic deserialization, you can get the value of each property individually. For example, you can get the value of the "userId" property using DocumentSnapshot's getString(String field) method:
val userId = snapshot.getString("userId")
Edit2:
Your three classes should look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue = FireFranValue(false),
var valorb: FireFranValueB = FireFranValueB(openPercent = 0)
)
data class FireFranValue(
var on: Boolean // = false
)
data class FireFranValueB(
var openPercent: Number // = 0
)
Edit3:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue? = null,
var valorb: FireFranValueB? = null
)
data class FireFranValue(
var on: Boolean? = null
)
data class FireFranValueB(
var openPercent: Number? = null
)
Now, "on" and "openPercent" will get the values from the database.
Please check the following class declaration:
You can define a no-arg constructor in kotlin like this:
data class SampleClass(
val field1: String,
val field2: String
) {
constructor(): this("", "")
}
First thing: It was not necessary to create the field (map type) called valorb. It was resolved with value.openPercent
As I have two documents (alarme and garagem) I created two POJO classes (FireFran and FireFranB)
https://github.com/neuberfran/JThingsFinal/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFranB.kt
The secret was to treat the value.on and value.openPercent fields as maps (as facts are):

Serializing Object with nested classes return empty String Kotlin

Im new to kotlin and android development. Im currently trying to get a POST request to a django REST-API in my local server. Im using Retrofit 2 and Gson for http request and JSON.
I have a class for POST body (DataPost) and goes like this:
class DataPost(_type: String, _attributes: Item) {
#SerializedName("type")
var type: String? = null
#SerializedName("attributes")
var attributes: Item? = null
}
class Item(_userId: Int, _dbId: Int, _title: String, _body: String){
#SerializedName("userdId")
var userdId: Int = 0
#SerializedName("dbId")
var dbId: Int = 0
#SerializedName("title")
var title: String? = null
#SerializedName("body")
var body: String? = null
companion object {
fun className(): String {return "Item"}
}
}
When I initialize it and try to serialize it and log it to JSON by:
var item: Item = Item(1,2,"Shiee","shie body")
var data: DataPost = DataPost("Item", item)
Log.i(TAG_LOGS, Gson.toJson(data))
I just get this output:
{}
And no error in console/log. So I assume its not seriliazing it property due the nested object, but as anything, I might be wrong. Why is returning an empty JSON my DataPost Object?
EDIT: Expected JSON:
{
"type": "Item",
"attributes": {
"userdId": 1,
"dbId": 2,
"title": "Shiee",
"body": "shiee body"
}
}
Okay so I just needed to delete de ? operator that marks the type as nullable since I didn't declare a serialization for nullable.
From:
#SerializedName("type")
var type: String? = null
#SerializedName("attributes")
var attributes: Item? = null
To:
#SerializedName("type")
lateinit var type: String
#SerializedName("attributes")
lateinit var attributes: Item
The arguments you pass to your classes are not used in any way.
For example, in DataPost you have _type as an argument in the constructor, and type as a property in the class. These two unrelated. So when you do
DataPost("Item", item)
"Item" and item are passed in the constructor as _type and _attributes, but then they're not used: in particular they are not automatically assigned to type and attributes.
I'm not sure how changing the types of the properties from nullable to non-nullble types is helping you, as it shouldn't.
What you need to do instead is either move your vars directly to the constructor or manually assign them.
Using your DataPost class as an example. You can either do:
class DataPost(
#SerializedName("type")
var type: String?,
#SerializedName("attributes")
var attributes: Item?
}
or
class DataPost(_type: String, _attributes: Item) {
#SerializedName("type")
var type: String? = _type
#SerializedName("attributes")
var attributes: Item? = _attributes
}
Side note: if these are immutable values you can change var to val.

Kotlin data class | add my own variables other than JSON keys

I'm using retrofit and kotlin for my andorid app.
For one of my APIs, I have the following data classes
class Set(val set_id: Long, val tickets: List<Ticket>, val timer: Int) {}
class Ticket(val ticket_id: Long, val rows: List<Row>) {}
class Row(val row_id: Long, val row_numbers: List<Int>) {}
Sample JSON DATA
{
"set_id": 60942,
"tickets": [
{
"ticket_id": 304706,
"rows": [
{
"row_id": 914116,
"row_numbers": [
0,
11,
21,
0,
42,
52,
0,
76,
85
]
}
]
}
],
"timer": 12
}
Set class contains a list of Ticket and each ticket has a list of Row
My JSON object contains only these values and it is working fine till here. Retrofit mapping is also working.
Problem:
I want to add my own a boolean field/variable isPlaying for class Ticket, which will be updated in the app later. But, this field should be set to true by default.
So I've tried this
class Ticket(val ticket_id: Long, val rows: List<Row>, var isPlaying: Boolean = true) {}
and this
class Ticket(val ticket_id: Long, val rows: List<Row>) {
var isPlaying: Boolean = true
}
NOTE: JSON doesn't have isPlaying key, I want it for app logic only.
Both did not work, isPlaying is always showing false. I want it to be true by default.
Please, help me out. Thank you!
Default arguments would not work in data class in the case when objects are instantiated with retrofit due to parsing library used underneath, which probably creates objects without calling constructor. For example Gson uses sun.misc.Unsafeto create objects.
What you can do - is to add backing property for fields that have default values:
class Ticket(
val ticket_id: Long,
val rows: List<Row>,
private var _isPlaying: Boolean? = true
) {
var isPlaying
get() = _isPlaying ?: true
set(value) {
_isPlaying = value
}
}
The root of the problem is the fact that GSON creates object not via constructor call, but via Unsafe staff. Thus it leads to broken things like null-safety or property initialization.
The only way I know how to workaround your case looks like this:
class Ticket(val ticket_id: Long, val rows: List<Row>) {
private var _isPlaying: Boolean? = null
var isPlaying: Boolean
get() = _isPlaying ?: true // <-- default value
set(value) {
_isPlaying = value
}
}
Here we create one backing property _isPlaying which will be initialized to null by GSON. Next we add custom getter and setter for public property which checks each it is invoked if _isPlaying is null. And if so it returns your default value. Otherwise, it returns the value stored in _isPlaying
Yes, it looks ugly. Otherwise you should consider using another json library like Jackson which as I know supports Kotlin well.
With using GsonBuilder setFeildNamePolicy
val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
val info = gson.fromJson(json, Account::class.java)
data class Account(
#SerializedName("user_name") var userName: String = "",
#SerializedName("ticket_id") var ticketId: String = "",
#SerializedName("row_id") var rowId: String = "")
}

Android Room library error: Cannot find setter for field. (Kotlin)

I am using room library and I have below mentioned entity:
#Parcelize
#Entity(tableName = "tb_option")
data class OptionsTable(
var question_id: Int? = null,
var option_id: Int? = null,
var option: String? = null,
var is_selected: Int? = null,
#PrimaryKey(autoGenerate = true)
var sr_no: Int = 0) : Parcelable
as you can see I have all the field declared as var but it is still showing error as:
error: Cannot find setter for field.
e:
e: private java.lang.Integer is_selected;
e:
^
please suggest some fix for this.
Thanks
Most of the time issue is occurring because of the following:
Problem 1:
Final field: Fields are marked with val, they are effectively final and don't have setter fields.
Solution: Replace the fields val with var. You might also need to initialize the fields.
Problem 2:
is keyword: We cannot use sqllite reserved keywords line for fields naming source e.g.
The following will cause error
#ColumnInfo(name = "IS_ACTIVE") var isActive
Solution: The solution is:
#ColumnInfo(name = "IS_ACTIVE") var active
I removed the initialization of sr_no from
#PrimaryKey(autoGenerate = true)
var sr_no: Int = 0
and the final code is:
#PrimaryKey(autoGenerate = true)
var sr_no: Int
worked for me because it was an auto-generated field.
There is an issue in room orm library with kotlin java code generation.
My optional field isFavorite and compile time same error then I change my field name to favorite then compiled.
before
var isFavorite: Int? = 0,
working fine:
var favorite: Int? = 0,
Thanks

Categories

Resources