I want to know what approach you will take to make data class in this situation, In this JSON service
I have subCategories under the other-services field. every item in the subCategories has a slug field which is also one of the field in other data elements like - digital-currency, picture, print. now my requirement is I want to pick up appropriate data class based on user selection of subCategories item. if the user has selected below item
{
"title": "電子マネー",
"slug": "digital-currency"
}
then I should be able to match the slug field and then should pick up the digital-currency data element
"digital-currency": {
"slug": "digital-currency",
"title": "電子マネー",
"_id": "7j6OzPKVzim7xvW8BvI8zV",
"isSub": true,
"parent": "other-services"
}
how can I make data class for this ?
You could simplify your code by..
data class NetworkResponse(
val otherServices: OtherServices,
val digitalCurrency: SubCategory,
val picture: SubCategory,
val print: SubCategory
) {
fun getUserSelectedCategory(slug : String) : SubCategory? {
return when (slug) {
"digital-currency" -> digitalCurrency
"picture" -> picture
"print" -> print
else -> null
}
}
}
data class OtherServices(val subCategory: List<SubCategory>)
data class SubCategory(val title: String, val slug: String)
Below is my solution , If I understand it properly , it is nothing but finding object of given type in a heterogeneous List. here DigitalCurrency, Picture, Print, pet-supplies,... so on , are all heterogeneous type. To pick any one of this item , you uses slug field as identifier, you must have mapping mechanism to pick right data object for given type with given json structure.
however solution of mine goes adding WHEN condition every time when new data element added at backend. I believe , as per the requirement and Json structure made here , this cannot be generalize to any extend where It can map to appropriate data class without any additional changes in Code in future.
if you think that Data class can be construct in such way that it doesn‘t need any change in code to accommodate newly added data , Please let me know.
data class NetworkResponse(
val otherServices: OtherServices,
val digitalCurrency: DigitalCurrency,
val picture: Picture,
val print: Print
) {
fun getUserSelectedCategory(slug : String) : BaseClass {
when (slug) {
"digital-currency" -> {
return digitalCurrency
}
"picture" -> {
return picture
}
"print" -> {
return print
}
}
}
}
data class OtherServices(val subCategory: List<SubCategory>) {
}
interface BaseClass
data class SubCategory(val title: String, val slug: String) : BaseClass
data class DigitalCurrency(val title: String, val slug: String) : BaseClass
data class Picture(val title: String, val slug: String) : BaseClass
data class Print(val title: String, val slug: String) : BaseClass
Related
This question already has an answer here:
com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type Data class object KOTLIN
(1 answer)
Closed 1 year ago.
I have read other comments on this same issue, but none of them has touched on a situation like mine
In mine, below describes how the data is structured:
{
"symbols":{
"alphabets":{
"a":{
"available":true,
"text":"A",
"timestamp":1.512686825309134E9
},
"b":{
"available":true,
"text":"B",
"timestamp":1.512687248764272E9
}"NameOfSymbols":"alphabets"
}
}
}
*The reason why mine is showing the error is that it can't convert the string "NameOfSymbols" : "alphabets" to the objects as specified in the data class
So, what can be done about it, I use Kotlin
Is there a way I can exclude that part of the children value while I only get the one that is specified in the data class?
Data Class
data class alphabets(
val name: Names,
var NameOfSymbols: String? = null) {
data class Names(
var available: Boolean? = null,
var text: String? = null,
var timestamp: Long? = null) {
}
}
This structure might work for your case (untested):
data class Message(
#PropertyName("symbols") val symbols: Symbols,
)
data class Symbols(
#PropertyName("alphabets") val alphabets: Alphabets,
)
data class Alphabets(
#PropertyName("a") val a: Alphabet,
#PropertyName("b") val b: Alphabet,
#PropertyName("NameOfSymbols") val nameOfSymbols: String,
)
data class Alphabet(
#PropertyName("available") val available: Boolean,
#PropertyName("text") val text: String,
#PropertyName("timestamp") val timestamp: Long,
)
Usage would be:
// in your ValueEventListener
override fun onDataChange(snapshot: DataSnapshot) {
val value = snapshot.getValue<Message>()
}
If you want to exclude your NameOfSymbols, you should remove it, and add the #IgnoreExtraProperties, like shown below:
#IgnoreExtraProperties
data class Alphabets(
#PropertyName("a") val a: Alphabet,
#PropertyName("b") val b: Alphabet,
)
NOTE, I used these versions of firebase database:
implementation 'com.google.firebase:firebase-database:19.7.0'
implementation 'com.google.firebase:firebase-database-ktx:19.7.0'
ok, After reading the documentation on Structuring Database on the firebase docs website, Structure your database
I realised that i didn't structure my database well, i should have regrouped them after specifying the name
like below
{
// This is a poorly nested data architecture, because iterating the children
// of the "chats" node to get a list of conversation titles requires
// potentially downloading hundreds of megabytes of messages
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"messages": {
"m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
"m2": { ... },
// a very long list of messages
}
},
"two": { ... }
}
}
Although, it said that structuring it that way isn't a nice way, it seems that's what works for me
I am new in Kotlin. I need your help to sort my mutableList in custom data class. I need to find the search element in the list and put into the top of list. Mostly the search element is in the last element. So I don't know how to filter that. Please give me some suggestions how to achieve that. For example
Data Class
data class Person(val firstName: String, val lastName: String)
data class Item(val gender: Int, val human: List<Human>)
data class Human(val id: Int, val person: List<Person>)
I entered some fake data
val people = mutableListOf(
Item(1,
listOf(
Human(
1,
listOf(
Person("Ragnar", "Lodbrok"),
Person("Bjorn", "Ironside"),
Person("Sweyn", "Forkbeard")
)
),
Human(
2,
listOf(
Person("Ragnar", "Lodbrok"),
Person("Bjorn", "Ironside"),
Person("Sweyn", "Forkbeard")
)
)
)
)
)
If i want to search Forkbeard and want to put in top of list. But i am unable to do this. So please suggest me some good advice.
I tried this but not working
people.forEach { people ->
people.human.forEach { human ->
human.person.sortedByDescending { person ->
person.lastName == "Forkbeard"
}
}
}
I am getting this
[Item(gender=1, human=[Human(id=1, person=[Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside), Person(firstName=Sweyn, lastName=Forkbeard)]), Human(id=2, person=[Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside), Person(firstName=Sweyn, lastName=Forkbeard)])])]
Answer
I want this
[Item(gender=1, human=[Human(id=1, person=[Person(firstName=Sweyn, lastName=Forkbeard),Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside)]), Human(id=2, person=[Person(firstName=Sweyn, lastName=Forkbeard),Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside)])])]
[ Sweyn Forkbeard, Ragnar Lodbrok, Bjorn Ironside ]
Thanks a lot
A trick to do all of this in just one line in Kotlin is with sortBy
people.sortByDescending { it.lastName == "Forkbeard" }
To get a single item from a list, you can use the first function:
val sweyn: Person = people.first { it.lastName == "Forkbeard" }
Then, for adding sweyn at the top:
people.remove(sweyn)
people.add(0, sweyn)
If you need to constantly do this, maybe you are looking for a Queue
EDIT:
For the nested human list, you can do the same if you change the person's type to MutableList in the Human data class:
people.forEach { people ->
people.human.forEach { human ->
val sweyn: Person = human.person.first { it.lastName == "Forkbeard" }
human.person.remove(sweyn)
human.person.add(0, sweyn)
}
}
I want to combine company and workFor variables into one stream, but I don't know how. I tried to use switchMap, zip, merge and concatMapIterable but nothing worked. Or I did something wrong..
My data model:
data class UserCompany(
#field:Json(name = "user") val user: User,
#field:Json(name = "company") val company: Company,
#field:Json(name = "workFor") val workFor: List<Company>
)
And my current code:
authApi.getCompany(getJwt()) //here I get data that looks like the above model
.subscribeOn(Schedulers.io())
.flatMapIterable {
return#flatMapIterable it.data.workFor.toMutableList().add(it.data.company) //error Type mismatch
},
//should returns stream of company and workFor
How to combine two variables from one request using Rx?
Edit, better example.
Let's say we have:
data class Pet(val name: String)
data class PetResult(
val myPet: Pet, //dog
val otherPet: List<Pet> //cat, bird, cow
)
And i wannt to get something like this:
authApi.getPet() // response looks like PetResult (model above)
.subscribeOn(Schedulers.io())
.subscribe(
{ petList.setSuccess(it) }, // should returns dag, cat, bird, cow
{ petList.setError(it.message) }
)
So, how to merge myPet with otherPet that i get from one API request?
You can achieve the above example like below:
apiClient.getPet.flatMap {
var getListResponse=it;
//From getListResponse you will get Result obj which contain pet and other pet list
var pet=it.getPet()
var petList=it.getPetList()
petList.add(pet)
getListResponse.setPetList=petList
}
I've got a list of, let's say food dishes.
The data class looks like this:
data class FoodItem(val categoryId: Int, val categoryTitle: String, val title: String, val text: String)
So let's say I have this is as data:
val list = listOf(FoodItem(1, "Mexican", "Burrito", "Wrapped food"),
FoodItem(2, "Italian", "Pizza", "Dough, cheese and tomato"),
FoodItem(3, "BBQ", "Brisket", "Does it need explaining?"),
FoodItem(4, "Japanese", "Sushi", "Raw fish and rice!"),
FoodItem(2, "Italian", "Pasta", "Great with meatballs!"))
I want this to be grouped into a single list, where the ones where CategoryId and CategoryTitle match, are grouped.
So in the end I would like a list of 4 items, where the Italian category has 2 items in its own list. So I would imagine I need a new data class that can hold a Category, which contains the id and title, and then a list of FoodDescription or something that contains Pizza, Pasta, etc etc, all within the category it's in.
I would like an output something similar to this:
GroupedFoodItemViewModel(category=Category(categoryId=2, categoryTitle=Italian), foods=[FoodItemViewModel(categoryId=2, categoryTitle="Italian", foodTitle="Pasta", foodText="Great with meatballs!"), FoodItemViewModel(categoryId=2, categoryTitle=Italian, foodTitle=Pizza, foodText=Dough, cheese and tomato)])
Where the rest would be in a list similar to this, just with one element under the "foods" array. I'd like to use this in a recyclerview, where I have the category as a title and the list of foods in below the category.
Assuming there is a 1:1 correspondence, I would not recommend having redundant properties for category ID and title. That is inviting error. You should have a constant map of IDs to titles somewhere, and the class can pull in the title that way:
val CATEGORY_IDS_TO_TITLES = mapOf(1 to "Mexican", 2 to "Italian", 3 to "BBQ", 4 to "Japanese")
data class FoodItem(val categoryId: Int, val title: String, val text: String) {
val categoryTitle: String get() = CATEGORY_IDS_TO_TITLES[categoryId] ?: error("Unknown category ID")
}
data class Category(val categoryId: Int) {
val categoryTitle: String get() = CATEGORY_IDS_TO_TITLES[categoryId] ?: error("Unknown category ID")
}
Then, given your class that combines a category with a list:
data class GroupedFoodItemViewModel(val category: Category, val items: List<FoodItem>)
you can use groupBy on your original list to collect the separate lists based on the category ID, and then map the entries of the generated Map<Int, List<FoodItem>> into a list of GroupedFoodItemViewModel:
val grouped = list.groupBy(FoodItem::categoryId)
.entries.map { (id, itemList) ->
GroupedFoodItemViewModel(Category(id), itemList)
}
I figured out a solution for you. You said specifically you want to match your CategoryId and CategoryTitle so that's exactly what I did:
val groupedMap = list.groupBy{ Pair(it.categoryTitle, it.categoryId) }
groupedMap.entries.first().key.first //for the name e.g. "Italian"
So basically Kotlin offers in its stdlib a function called groupBy where you can similarly to SQL group a List by a given key. In your case the Key consists of two Properties (CategoryId and CategoryTitle). To use both as a Key you can either create a custom Class containing those props or use - as I did - a Pair. The second line of the code accesses the first entry of the resulting list - then the key of it (e.g. Pair("Italian", 2)) and with the first at the end you access the first property of the Pair (in that case the CategoryTitle).
Since your CategoryId seems to be correlated with your CategoryTitle you might be able to only group by CategoryTitle. This would then look like this:
val groupedMap = list.groupBy{ it.categoryTitle }
Then the name (e.g. Italian) would be the key of the resulting groupedMap.
EDIT after changed requirements
So basically you could do this:
val result = list.groupBy{ foodItem ->
Category(foodItem.categoryId, foodItem.categoryTitle)
}
result.forEach { entry ->
entry.value.map { foodItem ->
with(foodItem) {
FoodItemViewModel(
categoryId,
categoryTitle,
title,
text
)
}
}
}
data class FoodItem(val categoryId: Int, val categoryTitle: String, val title: String, val text: String)
data class FoodItemViewModel(val categoryId: Int, val categoryTitle: String, val foodTitle: String, val foodText: String)
data class Category(val categoryId: Int, val categoryTitle: String)
All I am doing here is creating the Category (key) whilst grouping the elements. Then I only have to map the resulting value from FoodItem to FoodItemViewModel.
You may want to consider refactoring your classes a bit. The properties of Category also exist in FoodItem and FoodItemViewModel.
Hope this solves your problem :).
You can do it by groupBy.
Please check below code:
data class FoodItem(val categoryId: Int, val categoryTitle: String, val title: String, val text: String)
val list = listOf(FoodItem(1, "Mexican", "Burrito", "Wrapped food"),
FoodItem(2, "Italian", "Pizza", "Dough, cheese and tomato"),
FoodItem(3, "BBQ", "Brisket", "Does it need explaining?"),
FoodItem(4, "Japanese", "Sushi", "Raw fish and rice!"),
FoodItem(1, "Italian", "Sushi", "Raw fish and rice!"),
FoodItem(2, "Italian", "Pasta", "Great with meatballs!"))
val groupMap = list.groupBy{ Pair(it.categoryId , it.categoryTitle) }
println(groupMap)
Output:
{(1, Mexican)=[FoodItem(categoryId=1, categoryTitle=Mexican, title=Burrito, text=Wrapped food)],
(2, Italian)=[FoodItem(categoryId=2, categoryTitle=Italian, title=Pizza, text=Dough, cheese and tomato),
FoodItem(categoryId=2, categoryTitle=Italian, title=Pasta, text=Great with meatballs!)],
(3, BBQ)=[FoodItem(categoryId=3, categoryTitle=BBQ, title=Brisket, text=Does it need explaining?)],
(4, Japanese)=[FoodItem(categoryId=4, categoryTitle=Japanese, title=Sushi, text=Raw fish and rice!)],
(1, Italian)=[FoodItem(categoryId=1, categoryTitle=Italian, title=Sushi, text=Raw fish and rice!)]}
I'm receiving json like this
{
"animal" : {
"type" : "FIRE",
"food" : "potato",
"water": "7up"
}
}
---
{
"animal" : {
"type" : "WATER",
"water": "7up"
}
}
---
{
"animal" : {
"type" : "CYPO",
"counter": 7
}
}
---
{
"animal" : {
"type" : "UNKNOWN",
"food": "Stup",
"water": "Cola",
"counter" : 4
}
}
Am I suposed to create 4 data classes? I mean, I know how they are going to look, they are not going to change, but depends of the type is going to have some parameters or not, so do I have to create 4 data class like for instance the FIRE one
data class AnimalFireResponse{
#SerializedName("type") val myEnum: MyEnum, #SerializedName("food") val food : String, #SerializedName("water") val water : String)
}
But then I do not know what to put on my service
#GET("/dynamic/stuff.php")
fun getAnimal(): Call<List<MyResponseWithDynamicAnimal>>
Is there any other way way generic?
Edit
Maybe it helps but, I know all the type values (FIRE,WAITER,CYPO,UNKNOWN) and I know all of them are going to return the same values, for instance FIRE is going to return always food and water, does it help to you guys?
Yes, you can make a single data class to represent this JSON.
As the names of the fields don't change you can include them all and mark them as nullable.
I suppose you could create 1 data class with all the possible fields and use the fields accordingly. What i mean is:
data class AnimalResponse{
#SerializedName("type") val myEnum: MyEnum,
#SerializedName("food") val food : String= "",
#SerializedName("water") val water : String= "",
#SerializedName("counter") val counter : Integer = -1)
}
If your response doesn't contain a field, it will be initialized with a default value which you can check in your code. However, I feel like the API structure may not be well optimized.(I could be wrong, I am no expert on this)
Use the following DTO if nullable is ok
data class AnimalResponse(
val type: String?,
val food : String?,
val water : String?,
val counter : Integer?
)