Can't convert String to data class objects - Firebase realtime database [duplicate] - android

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

Related

Constructing Kotlin classes for using Gson to convert json response to class

I am beginning to learn Android development, and at the moment I am focusing on how to consume REST API's. The service that I am experimenting with (The Movie Database) provides a response of the form:
{
"certifications": {
"CA": [
{
"certification": "G",
"meaning": "All ages.",
"order": 1
},
...
],
"NL": [
{
"certification": "AL",
"meaning": "All ages.",
"order": 1
},
...
],
...
}
}
There are quite a long list of objects contained in the list (CA, NL, EN, etc.) but all of these objects contain the same thing - a list of objects of the exact same structure. Any of the online JSON to Kotlin class converters that I have tried want to create individual classes for each object which to me seems wasteful.
I currently have this class:
class Certifications {
var certifications: List<Country>? = null
}
class Country {
var country: String? = null
var ratings: List<Certification>? = null
}
class Certification {
var certification: String? = null
var meaning: String? = null
var order: Int = 0
}
Attempting this val thing = gson.fromJson(body, Certifications::class.java) results in Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 20 path $.certifications.
This :
class Certifications {
var certifications: List<Country>? = null
}
Should be something like :
class Certifications {
var certifications: Map<String, <List<Certification>>? = null
}
Where the String is the code "NL" & "CA" - or if you already have a custom type adapter you could do Map<Country, <List<Certification>> - but I'm unsure if you have this setup.

How to create kotlin data class for this network service?

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

Combine two variables in one request using Rx

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
}

Deal with dynamic response in retrofit

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?
)

Realm query by two params in list

I have two objects: Author and Book.
#RealmClass
class Author {
#PrimaryKey
val id: String?
val books: RealmList<Book> = RealmList()
}
#RealmClass
class Book {
#PrimaryKey
val id: String?
val countPages: Long
val genre: String
}
And I have data in realm, like this:
{
"id": "author1",
"books": [
{
"id": "book1",
"countPages": 100,
"genre": "fantasy"
},
{
"id": "book2",
"countPages": 150,
"genre": "non-fiction"
}
]
}
I want to find authors with books, which have specific genre and specific pages count. If I write something like this:
realmQuery.where().equalsTo("books.countPages", 100).equalsTo("books.genre", "non-fiction").find()
I'll get one author with id = author1. But it's not true, I should get empty list.
How can I write query to achieve this?
Link queries translate to has at least one of ___ where X is true, so
.equalsTo("books.countPages", 100).equalsTo("books.genre", "non-fiction")
Says "author that has at least one book that has countPages 100, and has at least one book that has genre non-fiction" -- which is true! But it is not what you want.
There are two ways to go about this:
1.) query the existing result set to get a "smaller" result:
realmQuery.where()
.equalTo("books.countPages", 100)
.findAll()
.equalTo("books.genre", "non-fiction")
.findAll()
2.) execute the query on Books, and access the Author via linking objects inverse relationship
#RealmClass
class Book {
#PrimaryKey
val id: String?
val countPages: Long
val genre: String
#LinkingObjects("books")
val authors: RealmResults<Author>? = null
}
And
val books = realm.where<Book>().equalTo("countPages", 100).equalTo("genre", "non-fiction").findAll();
// these books have `authors` field that contains the author

Categories

Resources