Entities and POJOs must have a usable public constructor in Kotlin - kotlin

I'm trying to make a Room entity by using #Ignore to a list but when i try to build the app i get the following error:
Entities and POJOs must have a usable public constructor
Here is how my code looks like:
#Entity(tableName = "prodotti_table")
data class Prodotto(
#PrimaryKey(autoGenerate = false)
var codart: String,
var desc: String,
var prezzo_acq: Float,
var prezzo_vend: Float,
#Ignore var barcode: List<Barcode>,
var qta: Float
)
#Entity(tableName = "barcode_prodotti_table")
data class Barcode(
#PrimaryKey
var id: Int,
var codart: String,
var barcode: String,
var qta: Float
)
How can i solve it still by using the #Ignore in that data class?

You can't put an ignored property in the constructor, so do this:
#Entity(tableName = "prodotti_table")
data class Prodotto(
#PrimaryKey(autoGenerate = false)
var codart: String,
var desc: String,
var prezzo_acq: Float,
var prezzo_vend: Float,
var qta: Float
) {
#Ignore var barcode: List<Barcode> = emptyList()
}
But remember that a property that isn't in the constructor of a data class will not participate in equals, hashcode, copy, or toString.

Related

How do I get context inside a companion object that is inside a data class Kotlin?

I'm trying to use a resource string inside a companion object that is inside a data class. but I don't Know how to obtain context in that case in Kotlin.
Anyone knows how to do it?
data class PhoneCall(
val type: String,
val code: String,
val description: String){
companion object{
const val SOLUTION_NO_SOLUTION = "NO_SOLUTION"
const val SOLUTION_TOMORROW = "71_INAT"
const val SOLUTION_TODAY = "72_INAT"
val solutions = listOf(
PhoneCall(Service.Traffic.PICK_UP, SOLUTION_NO_SOLUTION, Resources.getSystem().getString(R.string.makeService))
)
}
I need to use a resource string in the 3 parameter, but I'm not able to get the context.
You can modify you PhoneCall model to store a string resource id instead of the actual string.
data class PhoneCall(
val type: String,
val code: String,
#StringRes val description: Int
) {
companion object {
const val SOLUTION_NO_SOLUTION = "NO_SOLUTION"
const val SOLUTION_TOMORROW = "71_INAT"
const val SOLUTION_TODAY = "72_INAT"
val solutions = listOf(
PhoneCall(Service.Traffic.PICK_UP, SOLUTION_NO_SOLUTION, R.string.makeService)
)
}
}
Then, when you need to display this data in the UI (say a TextView), you can fetch the string from the resource id.
descriptionTextView.text = getString(phoneCall.description)

How to properly use class inheritance in Kotlin in combination with Kotlinx Serialization

I have a simple hierarchy containing of the following:
abstract class BaseItem
open class Item : BaseItem
class Backpack : Item
They should all work with Kotlinx Serialization. It went fine until I added the Backpack class.
I use version 1.4.32 of Kotlinx Serialization.
Here is my class hierarchy in detail
// Items.kt
#Serializable
sealed class BaseItem {
abstract val id: String
abstract val type: ItemType
abstract var brand: String
abstract var model: String
abstract var imageLink: String
abstract var traits: MutableList<Trait>
abstract var implicitTraits: MutableList<Trait>
abstract var details: MutableMap<String, String>
}
#Serializable
open class Item(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.UNDEFINED,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
) : BaseItem()
#Serializable // is marked as incorrect
class Backpack(
brand: String,
model: String,
imageLink: String,
traits: MutableList<Trait>,
implicitTraits: MutableList<Trait>,
details: MutableMap<String, String>,
var volume: Int
) : Item(
type = ItemType.BACKPACK,
brand = brand,
model = model,
imageLink = imageLink,
traits = traits,
implicitTraits = implicitTraits,
details = details
)
The IDE is showing me the following message for the #Serialization annotation attached to the Backpack class.
This class is not serializable automatically because it has primary constructor parameters that are not properties
I was not able to find out what the working way it is to make this correct
It's because the parameters of your constructor are not defined as properties of the class. To have the parameters defined as properties you have to add val or var to the parameters. This would resolve the error message you currently have:
#Serializable
class Backpack(
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
var volume: Int
) : Item(
type = ItemType.BACKPACK,
brand = brand,
model = model,
imageLink = imageLink,
traits = traits,
implicitTraits = implicitTraits,
details = details
)
But this would still not compile as you would end up with Serializable class has duplicate serial name of property 'brand', either in the class itself or its supertypes for all properties that are used in both classes. But anyhow, I am a little bit surprised by the design as it usually not a good practice to inherit from a non abstract class. Without knowing the intentions I am wondering whether sth.like this would not work for you as well:
sealed class BaseItem {
abstract val id: String
abstract val type: ItemType
abstract var brand: String
abstract var model: String
abstract var imageLink: String
abstract var traits: MutableList<Trait>
abstract var implicitTraits: MutableList<Trait>
abstract var details: MutableMap<String, String>
}
#Serializable
data class Item(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.UNDEFINED,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
) : BaseItem()
#Serializable
data class Backpack(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.BACKPACK,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
override var var volume: Int
) : BaseItem()
I btw. removed the #Serializable from BaseItem as it is unnecessary because the class is abstract anyhow and therefore won't be serialized at all. I also made your classes data class because my impression was that those basically exist to hold data and those are usually implemented with data class. I left the many var I see as I don't know the reasoning for those but a small hint from my side is that you should prefer val over var especially in data class. A var in this scenario feels like a code smell to me and you might want to consider using val instead. A good literatur for such things is the Kotlin page itself: https://kotlinlang.org/docs/coding-conventions.html#idiomatic-use-of-language-features
From Designing Serializable Hierarchy:
To make hierarchy of classes serializable, the properties in the parent class have to be marked abstract, making the [parent] class abstract, too.

Kotlin Data class setter using class fields

I have a redis entity using a kotlin data class and its Id should be a combination of few other fields from the same class.
Can this be achieved by defining setter for Id field instead of computing it outside of data class?
#RedisHash("Game")
data class Game(
#Id
val generatedId: String = "Default_ID",
val name: String,
val location: String,
val homeTeam: String,
val awayTeam: String
)
// want something like this
var generatedId : String = "DEFAULT_ID"
get() = "${name}${location}"
// or even better
var generated_Id : String = "${name}${location}"
Did you try to do something like this?
#RedisHash("Game")
data class Game(
val name: String,
val location: String,
val homeTeam: String,
val awayTeam: String,
#Id
val generatedId: String = "${name}${location}"
)

Kotlin data class + Gson: optional field

I have the following data class in Kotlin:
import com.google.gson.annotations.SerializedName
data class RouteGroup(
#SerializedName("name") var name: String,
#SerializedName("id") var id: Int
)
Sometimes I need to create an object with both fields, sometimes with only one of them.
How can I do this?
EDIT
This is not the duplicate of this question: Can Kotlin data class have more than one constructor?
That question shows how to set a default value for a field. But in my case, I don't need to serialize the field with the default value. I want a field to be serialized only when I explicitly assign a value to it.
it is easy you have to use the nullable operator
import com.google.gson.annotations.SerializedName
data class RouteGroup #JvmOverloads constructor(
#SerializedName("name") var name: String? = null,
#SerializedName("id") var id: Int? = null
)
You may need something like this:
sealed class RouteGroup
data class RouteGroupWithName(
#SerializedName("name") var name: String
) : RouteGroup()
data class RouteGroupWithId(
#SerializedName("id") var id: Int
) : RouteGroup()
data class RouteGroupWithNameAndId(
#SerializedName("name") var name: String,
#SerializedName("id") var id: Int
) : RouteGroup()
EDIT 1:
Or you can use nullable fields and named parameters like this:
data class RouteGroup(
#SerializedName("name") var name: String? = null,
#SerializedName("id") var id: Int? = null
)
val routeGroupWithName = RouteGroup(name = "example")
val routeGroupWithId = RouteGroup(id = 2)
val routeGroupWithNameAndId = RouteGroup(id = 2, name = "example")

Cannot save data model that contains List<Model> with Room ORM Kotlin

I have a problem with Room ORM working on Kotlin. My task is having ability to save and get data models RouteTemplateModel, that contains list of addresses of type AddressModel and object of class RouteModel that contains title of the specific route. Here is my code:
AddressModel.kt
#Entity(foreignKeys = arrayOf(
ForeignKey(entity = RouteModel::class,
parentColumns = arrayOf("routeId"),
childColumns = arrayOf("parentId"))))
data class AddressModel(
#PrimaryKey(autoGenerate = true)
var addressId: Long,
var parentId: Long,
var street: String,
var house: String,
var entrance: String,
var title: String){
constructor(): this(0, 0, "", "", "", "")
}
RouteModel.kt
#Entity
data class RouteModel(
#PrimaryKey(autoGenerate = true)
var routeId: Long,
var title: String) {
constructor() : this(0, "")
}
Here is my simple models, I found in documentation of Room that for creating relations between models I need to use #ForeignKey and #Relation
So with code samples in doc and tutorials I create RouteTemplateModel that contains object of RouteModel and list of AddressModels. Here is the class
RouteTemplateModel
class RouteTemplateModel{
private var id: Long = 0
#Embedded
private var routeModel: RouteModel = RouteModel()
#Relation(parentColumn = "routeId", entityColumn = "parentId")
private var addressList: List<AddressModel> = listOf()
constructor()
constructor(id: Long, routeModel: RouteModel, title: String,
addressList: List<AddressModel>){
this.id = id
this.routeModel = routeModel
this.addressList = addressList
}
fun getId(): Long{
return id
}
fun getRouteModel(): RouteModel{
return routeModel
}
fun getAddressList(): List<AddressModel>{
return addressList
}
fun setId(id: Long){
this.id = id
}
fun setRouteModel(routeModel: RouteModel){
this.routeModel = routeModel
}
fun setAddressList(addressList: List<AddressModel>){
this.addressList = addressList
}
}
So what`s a problem? I am getting such errors:
Error:The columns returned by the query does not have the fields [id]
in com.innotech.webcab3kotlin.model.RouteTemplateModel even though
they are annotated as non-null or primitive. Columns returned by the
query: [routeId,title]
And
Error:Type of the parameter must be a class annotated with #Entity or
a collection/array of it.
It is a real problem, because if my trying to fix first error and annotate in RouteTemplateModel id variable to return this column too, I need annotate class as Entity (like in second error), but when I do it I am getting an error
Error:Entities cannot have relations.
Here is AppDatabase.kt
#Database(entities = arrayOf(RouteModel::class, AddressModel::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getRouteDao(): RouteDao
}
and RouteDao.kt
#Dao
interface RouteDao {
#Query("SELECT routeId, title FROM RouteModel")
fun getAll(): List<RouteTemplateModel>
#Insert
fun insertAll(vararg models: RouteTemplateModel)
#Delete
fun delete(model: RouteTemplateModel)
}
Thats really confusing. Please, help me)
Your "parentId" column is capable of holding long value only, make its type to "Text" then create a TypeConverter from "List" to String and vice a versa for reference please have a look at link .