I have to use a json file in my app and I have defined a different data classes to represent the data itself. the json is stored locally in the app.
the json look like:
[
{
"id":987847,
"type":"FixtureUpcoming",
"homeTeam":{
"id":43,
"name":"Manchester City",
"shortName":"Man City",
"abbr":"MNC",
"alias":"t43"
},
"awayTeam":{
"id":8,
"name":"Chelsea",
"shortName":"Chelsea",
"abbr":"CHL",
"alias":"t8"
},
"date":"2019-02-10T16:00:00.000Z",
"competitionStage":{
"competition":{
"id":8,
"name":"Premier League"
}
},
"venue":{
"id":2691,
"name":"Etihad Stadium"
},
"state":"preMatch"
},
{
"id":1036495,
"type":"FixtureUpcoming",
"homeTeam":{
...
]
So I have created a data class which represent an element of this array as below:
data class FixtureItem(
var id: Int,
var type: String,
var homeTeam: Team,
var awayTeam: Team,
var date: String,
var competitionStage: List<Competition>,
var venue: Venue,
var state: String
)
and some other elements are also a defined class like Team or Venue..
data class Team(
var id: Int,
var name: String,
var shortName: String,
var abbr: String,
var alias: String
)
What is the best way to translate this json using data class ? overall, this json is display in a recyclingview.
Any idea how to properly extract it and use it in a recyclerview ?
Thanks
Personally I would use Moshi or KotlinX Serialization to parse your data into objects
Moshi adds a few extra dependencies required for kotlin use and KotlinX Serialization was made by the kotlin team in 100% kotlin plus supports kotlin multiplatform if that's of any interest. It is fairly new however and just went 1.0 a few weeks ago.
cant really go wrong with either though
Related
In the Request body below, the number of value "questionOne", "questionTwo", etc changes for each student. How can i dynamically generate request body to fit the changing value of the key and value.
Sample request one
"quiz": {
"name":"Jacob",
"sid": "STD_500",
"questionOne":"",
"questionTwo":""
}
Sample request two
"quiz": {
"name":"Annie",
"sid": "STD_200",
"questionOne":"",
"questionTwo":""
"questionThree":"",
"questionFour":""
}
Data class:
data class Quiz (
val name : String?,
val sid : String?,
val questions: HashMap<String, String>?
)
I suppose the only way would be to define quiz as being a HashMap instead of a Quiz object.
I'm guessing you now have a RequestBody somewhere something like this?
data class RequestBody(
val quiz: Quiz
)
Then change it to
data class RequestBody(
val quiz: HashMap<String,String>
)
But it's kind of a bad design like this, I suggest to work out with the backend a solution as proposed by Tornike's answer
From your description, this is a bad design decision from backend side
You should have one parameter questions on which you will pass list of Question classes like this
First create a separate data class Question
data class Question (
val key:String,
val value:String)
than set list of this data class as Type of questions parameter in a request model like this
data class Quiz (
val name : String?,
val sid : String?,
val questions:List<Question>
)
I'm assuming you are using Gson library for converting data classes to json and vice versa
Solution for given situation is to create Separate request models for each number of questions you send to BE,
BUT i would strongly advise not to do this and make backend guys to change how your api works
The questions should be in a json array. Example:
"quiz": {
"name":"Jacob",
"sid": "STD_500",
"questions" : [
{"key": "questionOne", "value": ""},
{"key": "questionTwo", "value": ""},
]
}
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'm using the 1.0.0 version of kotlin serialization but I'm stuck when I try to deserialize a "flexible" array.
From the Backend API that I don't control I get back an JSON Array that holds different types of objects. How would you deserialize them using kotlin serialization?
Example
This is the API's response
[
{
"id": "test",
"person": "person",
"lastTime": "lastTime",
"expert": "pro"
},
{
"id": "test",
"person": "person",
"period": "period",
"value": 1
}
]
#Serializable
sealed class Base {
#SerialName("id")
abstract val id: String
#SerialName("person")
abstract val person: String
}
#Serializable
data class ObjectA (
#SerialName("id") override val id: String,
#SerialName("title") override val title: String,
#SerialName("lastTime") val lastTime: String,
#SerialName("expert") val expert: String
) : Base()
#Serializable
data class ObjectB (
#SerialName("id") override val id: String,
#SerialName("title") override val title: String,
#SerialName("period") val period: String,
#SerialName("value") val value: Int
) : Base()
Performing the following code result in an error
println(Json.decodeFromString<List<Base>>(json))
error Polymorphic serializer was not found for class discriminator
When you say you don't control the API, is that JSON being generated from your code by the Kotlin serialization library? Or is it something else you want to wrangle into your own types?
By default sealed classes are handled by adding a type field to the JSON, which you have in your objects, but it's a property in your Base class. In the next example it shows you how you can add a #SerialName("owned") annotation to say what type value each class corresponds to, which might help you if you can add the right one to your classes? Although in your JSON example both objects have "type" as their type...
If you can't nudge the API response into the right places, you might have to write a custom serializer (it's the deserialize part you care about) to parse things and identify what each object looks like, and construct the appropriate one.
(I don't know a huge amount about the library or anything, just trying to give you some stuff to look at, see if it helps!)
#cactustictacs solution came very close. He said that "By default sealed classes are handled by adding a type field to the JSON"
But because I didn't had a type property I needed a other field that decides which subclass it should be.
In Kotlin Serializer you can do that by
val format = Json {
classDiscriminator = "PROPERTY_THAT_DEFINES_THE_SUBCLASS"
}
val contentType = MediaType.get("application/json")
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(format.asConverterFactory(contentType))
.build()
where in classDiscriminator you can enter the property that you want. Hope this helps other people in the future.
I know there's a lot of other post regarding type converter, but I am facing a different scenario here.
What's different is actually the Json format that I am receiving from my server, and I cant ask them to update their format just so I can fix this issue.
I know Im just missing something or just doing it wrong
Here's my sample Json format
[{"name":"John","age":30,"cars":["Ford","BMW","Fiat"]}]
The cars format is not object, but JSON Array, and I kinda want to store it under List format in Kotlin
#Entity(tableName = "person")
data class Person(
#PrimaryKey
var name: String,
var age: Int,
#SerializedName("cars")
#TypeConverters(ListTypeConverter::class)
var cars: Array<Car>
)
Any help will be appreciated.
Thanks all
Your POJO it not correct, check below one
#Entity(tableName = "person")
data class Person(
#PrimaryKey
var name: String,
var age: Int,
#SerializedName("cars")
#TypeConverters(ListTypeConverter::class)
var cars: Array<String>
)
cars is a list of String, not Object
Use
var cars = mutableListOf<String>()
then add JSONArray data inside it
I have a JSON response of an API REST call that I am not pretty sure how should I deserialize...
{
.....
"date": "10-10-19",
"rates": {
"GBP" : 101.01,
"EUR" : 102.01,
"AUD" : 103.4,
......
}
}
I would like to know How could I deserialize the "rates" object?. I think it was a Map object so using Gson I make the next POJO:
class POJO(
private val base: String,
private val date: Date,
private val rates: Rate
)
And my Rate class is
class Rate ( private val currency : Map <String, Double> )
It doesn't make any problem unless I try to use this Map in my class. When I try to access to this variable for example here :
view?.converterBinder!!.setCurrencyList(it.data!!.rates.currency)
currency is null because I think Gson doesn't know how to resolve it. I don't know if I had to deserialize it manually or there are any solution for this using Gson.
Any thoughts??
The provided Json is completely wrong, this is how it should be formatted
{
"date": "10-10-19",
"rates": {
"GBP": 101.01,
"EUR": 102.01
}
}
Please check with https://jsonlint.com to confirm the validity of a Json.
So you have a json object with a String "date", then you have another json object called "rates" containing 2 numeric Doubles "GBP" and "EUR".
Each Json should be represented by a class, so to parse it create the following object containing the 2 classes
object Models {
data class Rates(#SerializedName("GBP") val gbp: Double,
#SerializedName("EUR") val eur: Double)
data class ExchangeRates(#SerializedName("date") val date: String,
#SerializedName("rates") val rates: Rates)
}
Now you pass the class ExchangeRates to Gson to deserialize your object and you should have all the data in place.