Customise single data class without creating multiple data classes in Kotlin - android

I have a scenario. I have created a data class in Kotlin like this:
data class AgentDetails(
val mobileNo: String,
val Name: String,
val Email: String,
val Password: String,
val Occupation: String,
val pincode: String,
val usertype: String,
val profilepic: String,
val AccountStatus: String
)
I want to send different type of objects of this data class to a web service:
1st object example:
val agentDetails = AgentDetails(mobileNo = mobileNumberText.text.toString(),
Name = userNameText.text.toString(),
Email = emailIdText.text.toString(),
Password = HashUtils.sha1(passwordText.text.toString()),
Occupation = item,
pincode = pinCodeText.text.toString(),
usertype = "Agent",
profilepic = "null", AccountStatus = "pending")
In 2nd object I only want to send mobile number. I dont wanna include any other field. Something like this:
val agentDetails = AgentDetails(mobileNo = mobileNumberText.text.toString())
And in 3rd object I only wanna send email id. Instead of creating multiple data classes. Can I use the same data class for multiple implementations?

Personally, I'd define three objects because they represent three different concepts (or projections of a concept). But if you make your properties nullable and provide a default value of null, you can get away with creating them as you want...
data class AgentDetails(
val mobileNo: String? = null,
val name: String? = null,
val email: String? = null,
val password: String? = null,
val occupation: String? = null,
val pincode: String? = null,
val usertype: String? = null,
val profilepic: String? = null,
val accountStatus: String? = null
)
Note: I've changed some of your property names to camelCase, as is the proper convention. And these all work fine:
AgentDetails(mobileNo = mobileNumberText.text.toString())
AgentDetails(email = "foo#example.com")
AgentDetails(name = "Foo", password = "Bar")
All of the other fields not provided will be null, and the types will be nullable, so you'll have to guard against that. Otherwise, I'd define three data classes for this.
Another solution would be to consider a sealed class structure:
sealed class AgentDetails
data class AgentByName(val name: String) : AgentDetails()
data class AgentByEmail(val email: String): AgentDetails()
// etc..
And then use it in a when expression:
fun doSomethingWithAgents(agentDetails: AgentDetails) {
when (agentDetails) {
is AgentByName -> // Do something
is AgentByEmail -> // Do Something
}
}

The easiest way is to make the fields nullable and provide default values:
data class AgentDetails(
val mobileNo: String? = null,
val Name: String? = null,
val Email: String? = null,
val Password: String? = null,
val Occupation: String? = null,
val pincode: String? = null,
val usertype: String? = null,
val profilepic: String? = null,
val AccountStatus: String? = null
)

Related

Kotlin Serialization #SerialName not working for Boolean

I'm using GSON to serialize some platform data. When I use #SerialName to capture platform data with a different naming convention in my app, it works for other types, but not Boolean types. As a simple example, if I have a class like...
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
#Serializable
data class Person (
#SerialName("first_name") val firstName: String? = null,
#SerialName("last_name") val lastName: String? = null,
val age: Int? = null
)
... everything works fine. The serializer finds first_name, last_name and age in the data and properly set the properties for the Person.
However, when I try to add a Boolean...
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
#Serializable
data class Person (
#SerialName("first_name") val firstName: String? = null,
#SerialName("last_name") val lastName: String? = null,
val age: Int? = null,
#SerialName("can_sing") val canSing: Boolean? = null
)
... the serializer does not catch and assign can_sing. It is strange that it works with a String but not a Boolean. Can any explain why I am seeing this behavior? I can work around this (for example, I can do val can_sing: Boolean? = null and it works), but I'm just wonder why #SerialName doesn't seem to work for a Boolean, or if I'm just missing something obvious.
You are mixing the Gson and Kotlin annotation types - Gson uses #SerializedName not #SerialName. I am not sure how your string types even work in that case (maybe something in how you call Gson that isn't included in the question).
As an example, the first class here (Person) can be serialized with the Kotlin serialization library, the second with Gson:
Kotlin annotations
#Serializable
data class Person (
#SerialName("first_name") val firstName: String? = null,
#SerialName("last_name") val lastName: String? = null,
val age: Int? = null,
#SerialName("can_sing") val canSing: Boolean? = null
)
Gson Annotations
data class PersonGson (
#SerializedName("first_name") val firstName: String? = null,
#SerializedName("last_name") val lastName: String? = null,
val age: Int? = null,
#SerializedName("can_sing") val canSing: Boolean? = null
)
Examples
Running this unit test with the Kotlin serialization library:
#Test
fun testJsonKotlin() {
val test = Person("hello", "world", 42, false)
val json = Json.encodeToString(test)
println(json)
val t2 = Json.decodeFromString<Person>(json)
println(t2)
}
produces the expected output:
{"first_name":"hello","last_name":"world","age":42,"can_sing":false}
Person(firstName=hello, lastName=world, age=42, canSing=false)
Doing that with Gson
#Test
fun testJsonGsonMixed() {
val testp = Person("hello", "world", 42, false)
val json = Gson().toJson(testp)
println(json)
val t2 = Gson().fromJson(json, Person::class.java)
println(t2)
}
technically works, but ignores the serialized name annotations (for all the cases, not just the boolean)
{"firstName":"hello","lastName":"world","age":42,"canSing":false}
Person(firstName=hello, lastName=world, age=42, canSing=false)
Using the Gson-annotated class with Gson
#Test
fun testJsonGson() {
val test = PersonGson("hello", "world", 42, false)
val json = Gson().toJson(test)
println(json)
val t2 = Gson().fromJson(json, PersonGson::class.java)
println(t2)
}
gives the correct response again
{"first_name":"hello","last_name":"world","age":42,"can_sing":false}
PersonGson(firstName=hello, lastName=world, age=42, canSing=false)

MutableList<Any> addAll kotlin entire arraylist added as object

Following is my code
var items : MutableList<Any> = arrayListOf()
items.add(TeacherDetails(it?.photo,it?.firstName,it?.lastName,it?.level))
items.add(TeacherBio(it?.bio))
items.add(TitleAccreditations(getString(R.string.acreditations)))
items.add(SessionsTitle(it?.firstName + getString(R.string.apostrophe) + getString(
R.string.sessions)))
items.addAll(listOf(it?.classes ?: arrayListOf()))
items.add(IntroVideo(it?.introVideo))
items.addAll(it?.teachingAccreditations?.split("\n")?.map { Accreditation(
it
) }?: emptyList())
Issue is at following line it is adding entire list as object instead of individual item.
items.addAll(listOf(it?.classes ?: arrayListOf()))
Following is my model
data class Teacher(
#field:SerializedName("firstName")
val firstName: String? = null,
#field:SerializedName("lastName")
val lastName: String? = null,
#field:SerializedName("teacherId")
val teacherId: String? = null,
#field:SerializedName("introVideo")
val introVideo: String? = null,
#field:SerializedName("level")
val level: String? = null,
#field:SerializedName("teachingAccreditations")
val teachingAccreditations: String? = null,
#field:SerializedName("classes")
val classes: List<ClassesItem?>? = null,
#field:SerializedName("photo")
val photo: String? = null,
#field:SerializedName("bio")
val bio: String? = null
)
I guess because your type of list is Any, it will consider adding list instance also as an object, so instead it as listOf() just add directly
items.addAll(it?.classes?.filterNotNull()?: arrayListOf())

How to store Multiple list of objects in room - Android

I have a doubt about creating tables for my network response.
My network response will be like:
data class ProductsList(
val next: String? = null,
val perPage: Int? = null,
val previous: String? = null,
val count: Int? = null,
val results: Results? = null
)
data class Results(
val prices: List<PricesItem?>? = null,
val products: List<ProductsItem?>? = null
)
data class ProductsItem(
val image: String? = null,
val content: List<Int?>? = null,
val prices: List<Int?>? = null
)
I'm confused the I should have one table or many tables. I'm beginner to room and SQL so if you have any resource to learn more about the room and data handling that will be very helpful for me.
Thanks in advance.

How to accomodate different arguments for entity class

I am calling my entity class in an if/else-if statement for 2 different conditions and the values I pass into the parameters depend on the condition. In the if block, I am passing 3 parameters and in the else-if block, I am passing 4. The entity's object is throwing an error because it is expecting 4 parameters. I want the first parameter to be optional and I'd like to know if there is a way to do that in Kotlin.
This is my entity class:
#Entity(tableName = "entry_table")
data class Entry(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
val username: String? = null,
val hint: String? = null,
val password: String? = null)
And this is the if/else-if block where I'm inserting values into the entity object:
if (requestCode == ADD_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
...
val entry = Entry(username, hint, password)
...
} else if (requestCode == EDIT_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
...
val entry = Entry(id, username, hint, password)
...
}
In Java, you could solve this problem by creating 2 constructors with matching number of parameters but I wonder if we can do the same in Kotlin or if there is a different approach.
You can try moving the id to the end, like so:
#Entity(tableName = "entry_table")
data class Entry(
val username: String? = null,
val hint: String? = null,
val password: String? = null,
#PrimaryKey(autoGenerate = true)
var id: Int? = null)
And then creating it like:
val entry = Entry(username, hint, password, id)
Or, if you want to keep the id as the first parameter, you can use named arguments like this:
val entry = Entry(username = username, hint = hint, password = password)
Hope that helps!
You can and probably should use multiple constructors.
The downside with your approach is that all the properties must be optional for technical reasons. But I guess, username and password actually are mandatory. The consequences are loss of compile time checks (and therefore prone to NPEs) and cumbersome reading (!! needed when accessing username).
You can avoid these problems following this approach:
To make your class have the default no-arg constructor, that is required by JPA, use the compiler plugin https://kotlinlang.org/docs/reference/compiler-plugins.html#jpa-support
Make mandatory properties non-nullable (I assume, only hint is optional)
Use as many secondary constructors as you like
#Entity(tableName = "entry_table")
data class Entry(
#PrimaryKey(autoGenerate = true)
val id: Int,
val username: String,
val hint: String?,
val password: String
) {
constructor(
username: String,
hint: String = null,
password: String = "change it"
) : this(0, username, hint, password)
}
In a more realistic scenario, only id and username were immutable. password and hint should be allowed to change.
#Entity(tableName = "entry_table")
data class Entry(
#PrimaryKey(autoGenerate = true)
val id: Int,
val username: String
) {
constructor(
username: String,
) : this(0, username)
var hint: String? = null
var password: String = "change it"
}

Can I using one Model for two entities in Room persistence?

I would like to know that, is it possible to use one model for two entities while using Room Persistence in Kotlin?
For example, I have to create two pagers which are general page and star page.
Both of them have fields following..
id: String?, caption: String?, imageUrl:String?, isUserLike: Boolean
(And more)
Data came from two separate services and I want to save it separately.
How can I do this, or there have any ways to solve this problem?
Thank you
P.S. I'm the beginner of Kotlin and Room persistence.
You can create a separate abstract class that contains all those common fields
abstract class BaseModel(
#PrimaryKey
var id: String? = null,
var caption: String? = null,
var imageUrl: String? = null,
...
) {
...
}
then have that abstract class be extended on the other classes
#Entity(tableName = "a_model")
data class AModel (
mId: String? = null,
mCaption: String? = null,
mImageUrl: String? = null,
...
): BaseModel(id = mId,
caption = mCaption,
imageUrl = mImageUrl,
...) {
...
}
you can also add other fields that are exclusive to that entity
#Entity(tableName = "b_model")
data class BModel (
mId: String? = null,
mCaption: String? = null,
mImageUrl: String? = null,
...
var nonCommonField: String? = null, // like this
): BaseModel(id = mId,
caption = mCaption,
imageUrl = mImageUrl,
...) {
...
}
Android Room will require fields to have a default value for each field or an empty constructor, if I remember correctly.

Categories

Resources