Android Retrofit - parse objects as list - android

I am using retrofit to get data from thing speak. And response that I am getting is containing JSON objects like "field1", "field2" etc. Is it possible to parse this data by Retrofit to get list containing of this elements?
For now I have parsing made like this:
#SerializedName("field1")
val field1: Float?,
#SerializedName("field2")
val field2: Float?,
#SerializedName("field3")
val field3: Float?,
#SerializedName("field4")
val field4: Float?,
#SerializedName("field5")
val field5: Float?,
#SerializedName("field6")
val field6: Float?,
#SerializedName("field7")
val field7: Float?,
#SerializedName("field8")
val field8: Float?
Response that I am getting:
{"created_at":"2019-05-24T06:11:43Z","entry_id":15419693,"field1":"370","field2":"56.390658174097666"}
And I would like to get something like this:
#SerializedName("field[]")
val fields List<Float>
Is that possible somehow?

You could create a custom JSON parser, but it isn't recommended. It's much better to use Retrofit parser.
You can't parse as you described, but it would be much better if you could update server response, so it looks like this:
{"created_at":"2019-05-24T06:11:43Z","entry_id":15419693,"fields":["370",:"56.390658174097666", "45"]}
And then in your class define
#SerializedName("fields")
var fields: List<Float>?
EDIT:
If you can't update server response, everything you can do is to define helper function:
public fun getFields() : ArrayList<Float?> {
var list = ArrayList<Float?>()
list.add(field1)
list.add(field2)
list.add(field3)
list.add(field4)
list.add(field5)
list.add(field6)
list.add(field7)
list.add(field8)
list.removeIf { it == null }
return list;
}

Related

retrofit generate unformatted complex json object

I am having a very strange problem
I am trying to build a complex json object which contains nested json
I have created it but when I send the request it crash
here is the format that I need
{"JR":"{"Text":"","userID":4,"Context":"","ClinicId":1}","TO":"getWatsonMessage","ver":2}
what I found in the request body as following:
{"JR":"{\"Text\":\"\",\"userID\":4,\"Context\":\"\",\"ClinicId\":1}","TO":"getWatsonMessage","ver":2}
which make the app crash
here is my code
data class MyBody #JvmOverloads constructor(
#SerializedName("ver") val version: Int,
#SerializedName("TO") val methodName: String,
#SerializedName("JR") val jsonParams: String,
#SerializedName("Method") var methodType: String? = null
)
and this is how I create the sub json object
val jsonJR = JSONObject()
jsonJR.put("Text",text)
jsonJR.put("userID",userId)
jsonJR.put("Context",context)
jsonJR.put("ClinicId",clinicId)
can anyone please help ?
It seems like the problem is with your JR of type String.
All the other params are in the proper format right?
So, I suggest that you provide type JR to another custom data class like
data class JRDataClass{
Text : String,
//and the rest of the data types
}

How to parse a data class into JSON string for Kotlin?

I wonder what is the way to convert a Kotlin data class into its equivalent json string. Json keys should be configurable.
Let's say I have a class,
data class Student(name: String?, roll: Int?, mark: Int?) {
}
I want to make a Json from this Student object where keys will be,
stundent_name, stundent_roll, stundent_mark
Moreover, I may also need to make a json from list of student with key students. How can I do so? I know using Gson I can create object from json string. How to do the reverse?
data class Student(
#SerializedName("stundent_name")
val name: String?,
#SerializedName("stundent_roll")
val roll: Int?,
#SerializedName("stundent_mark")
val mark: Int?
)
And the code for convertion is:
val gson = Gson()
val student = Student("John", 1, 5)
gson.toJson(student)
This code makes String like this:
{"stundent_mark":5,"stundent_name":"John","stundent_roll":1}
And if you need to create JsonArray, just do the same with your List of students:
gson.toJson(list)

Retrofit - Android - Handling multiple type keys and values with gson

So I've been having some trouble with an api that I want to consume from my application when dealing with multiple types from the keys and values of the response json.
Let me show you the json response:
{
"error":[
],
"result":{
"field1":[
[
1544258160,
"57.15",
"57.15",
"57.15",
"57.15",
"0.00",
"0.00000000",
0
],
[
1544258220,
"56.89",
"56.89",
"56.89",
"56.89",
"56.89",
"2.94406281",
1
]
],
"field2":1544301240
}
}
and here is the representation of the pojo class:
data class Response(val error: List<String>, val result: LinkedTreeMap<String, List<List<Result>>>)
data class Result(
val time: Double,
val open: String,
val high: String,
val low: String,
val close: String,
val vwap: String,
val volume: String,
val count: Double
)
I know that the current structure fails to represent the json format. but I have run out of ideas.
btw the stack error is saying this:
Expected BEGIN_OBJECT but was NUMBER
edit: adding a bit more context
I'm using Gsonconverter for the retrofit builder.
val retrofit = Retrofit.Builder().baseUrl(API_URL).client(client)
.addConverterFactory(GsonConverterFactory.create()).build()
You could build a Gson JsonDeserializer for the filed1, filed2 types.
It is more manually written code, but this way you can check the type of the filed and call the right deserializer.
yeah,what make the error is your response field(result),it's different type.however.you just use the first type Response(val error: List<String>, val result:LinkedTreeMap<String, List<List<Result>>> receive all the json.
in my view,you can use below ways to solve it.
First,redefine you receive model,maybe you can use the Gson JsonObject and when deal with value remember check the type.
Second,discuss with your background server engineer,agree on the every type response.may be the "field2" can be "field2:[[]]"
Next,change your model.may be you define below it
#Serialized("field1")
LinkedTreeMap<String, List<List<Result>>> field;
#Serialized("field2")
String fields;
hope to help you.
Your problem is here:
"field2":1544301240
You defined like below:
val result: LinkedTreeMap<String, List<List<Result>>>
but instead of that you get number!!!
EDIT
Just try this:
data class Response(val error: List<String>, val result: YourModel)
data class YourModel(val field1: LinkedTreeMap<String, List<List<Result>>>, val field2: Double)
data class Result(
val time: Double,
val open: String,
val high: String,
val low: String,
val close: String,
val vwap: String,
val volume: String,
val count: Double
)
it has been a while but still i already came to a conclusion of what i need.
data class OHLCResponse(
private val error: List<String>,
val result: LinkedTreeMap<String, Any>)
turns out that on the LinkedTreeMap class i just needed to pass the second class type parameter as Any and it can be down casted to anything that you need.
also i leave here a nice tool that can help you map your json responses into kotlin plain objects/DTOs:
https://app.quicktype.io/
thanks all for your responses.
I solved the problem with such a solution in adapter class .
override fun getItemCount(): Int = leagueTableList[0].size
override fun onBindViewHolder(holder: LeagueTableViewHolder, position: Int) {
val gs = Gson()
val js = gs.toJson(leagueTableList[0][position])
val standing = gs.fromJson(js, Standing::class.java)
holder.view.table = standing
holder.bind(standing,onItemClick)
}

Kotlin - Retrofit request using RxJava gives null response

I'm trying to get news from Guardian API. I'm getting null response, everything is below. I'm using Kotlin, Retrofit and RxJava. I know that there are some miscalled variables/objects but I will change them when I will get rid of that problem.
Retrofit interface
#get:GET("search?api-key=test")
val news:Observable<News>
Retrofit client
val instance : Retrofit
get() {
if (myInstance == null) {
myInstance = Retrofit.Builder()
.baseUrl("https://content.guardianapis.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
return myInstance!!
}
And function where I'm loading data
private fun loadUrlData() {
compositeDisposable.add(jsonApi.news
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe{news -> displayData(news)})
}
JSON example
{
response:{
status:"ok",
userTier:"developer",
total:2063064,
startIndex:1,
pageSize:10,
currentPage:1,
pages:206307,
orderBy:"newest",
results:[
{
id:"politics/2018/sep/24/keir-starmer-labour-does-not-rule-out-remaining-in-eu",
type:"article",
sectionId:"politics",
sectionName:"Politics",
webPublicationDate:"2018-09-24T18:57:48Z",
webTitle:"Keir Starmer: Labour does not rule out remaining in EU as option",
webUrl:"https://www.theguardian.com/politics/2018/sep/24/keir-starmer-labour-does-not-rule-out-remaining-in-eu",
apiUrl:"https://content.guardianapis.com/politics/2018/sep/24/keir-starmer-labour-does-not-rule-out-remaining-in-eu",
isHosted:false,
pillarId:"pillar/news",
pillarName:"News"
}
]
}
}
Model class
data class News( val status: String, val userTier: String, val total: Int, val startIndex: Int, val pageSize: Int, val currentPage: Int, val pages: Int, val orderBy: String, val results: List<Result>)
I suppose that the problem is with the last function or with the interface but I can't find the solution.
The issues lies within your data model class.
Your JSON has an outer node (response) and if you're trying to return a News you won't get it, because Retrofit can't map the JSON to the News class. Add an outer class called Response that holds a field called response that is of type News, that should fix it.
Like so:
class Response(val response: News)
Note: I haven't added data in front of the class as you don't necessarily need it. The data keyword just adds some extra things for you automatically, like toString(), equals() and hashCode(), but unless you're actually using them for anything I wouldn't recommend adding the data keyword as it's pretty useless then.

Pulling data from an API with okHTTP & GSON

New to using API's (and kotlin for that matter) and I'm having some trouble figuring out how to pull data from an API into model objects and was hoping someone could point me in the right direction. Sample code below.
val request = Request.Builder().header("X-Mashape-Key", keyVal).url(url).build()
//make request client
val client = OkHttpClient()
//create request
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call?, response: Response?) {
//grab as string, works fine
val body = response?.body()?.string()
//make my builder, works fine
val gson = GsonBuilder().create()
// to pass type of class to kotlin ::
val cardFeed = gson.fromJson(body, CardFeed::class.java)
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
}
})
}
All of that seems to work as intended in debug, I get the string, and can see it but using the following it still dumps a null into the cardFeed/cards object.
class CardFeed(val cards: List<Card>)
class Card(val cardId: String, val name: String, val text: String, val flavor: String)
The body string I'm getting from the API reads as follows
body: "{Basic":[{"key":"value", etc
I'm assuming the [ is what's tripping me up, but not sure how to correct it.
Any ideas or nudges in the correct direction would be greatly appreciated and thanks ahead of time.
According to your class structure, the JSON object that you should get from the API should be
{
"cards":[
{
"cardId":"<cardID>",
"name":"<name>",
"flavor":"<flavor>"
},
{
"cardId":"<cardID>",
"name":"<name>",
"flavor":"<flavor>"
}
]
}
with the correct keys because when you use gson.fromJson(body, CardFeed::class.java), it looks for the variable names in the class assigned (CardFeed.java). If you don't have the correct keys ("cards", "cardId", "name", "flavor"), gson would return null for the values
okHttp is a low level library. In order to consume JSON based APIs, Retrofit is a much more suitable library, as it already have converters that use libraries like GSON or Moshi to do all the heavy lifting for you.

Categories

Resources