Android Retrofit - How to parse Github Emojis API - android

Please check this api: https://api.github.com/emojis
This is part of the response:
{
"+1": "https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png?v8",
"-1": "https://github.githubassets.com/images/icons/emoji/unicode/1f44e.png?v8",
"100": "https://github.githubassets.com/images/icons/emoji/unicode/1f4af.png?v8",
"1234": "https://github.githubassets.com/images/icons/emoji/unicode/1f522.png?v8",
"1st_place_medal": "https://github.githubassets.com/images/icons/emoji/unicode/1f947.png?v8",
"2nd_place_medal": "https://github.githubassets.com/images/icons/emoji/unicode/1f948.png?v8",
"3rd_place_medal": "https://github.githubassets.com/images/icons/emoji/unicode/1f949.png?v8",
"8ball": "https://github.githubassets.com/images/icons/emoji/unicode/1f3b1.png?v8",
"a": "https://github.githubassets.com/images/icons/emoji/unicode/1f170.png?v8",
"ab": "https://github.githubassets.com/images/icons/emoji/unicode/1f18e.png?v8",
"abacus": "https://github.githubassets.com/images/icons/emoji/unicode/1f9ee.png?v8",
"abc": "https://github.githubassets.com/images/icons/emoji/unicode/1f524.png?v8",
"abcd": "https://github.githubassets.com/images/icons/emoji/unicode/1f521.png?v8",
}
I'd like to convert this response to a list of Emoji.
data class Emoji(
val name: String,
val url: String,
)
Note that the response is a big object and I need a list.
This is how I'm instantiating Retrofit:
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
How could I achieve it?

You didn't attach you Api interface but based on your descriptions you've put List<Emoji> in you api interface which ia going to raise a MalformedJSONException
Use a Map<String, String> instead and if you need a list use responseMap.map{ Emoji(it.key, it.valie) }

Related

Parsing a jsonrray into object using moshi with Retrofit and Kotlin

I am trying to parse using Moshi Library for JSON Array using Kotlin Coroutines .
Code use
fun retrofitIndia(baseUrl : String) : Retrofit = Retrofit.Builder()
.client(clientIndia)
.baseUrl(baseUrl)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
I get issue while Parsing the data class for JSON Array . I have used same for JSON Object and it works fine but during array , it crashes
Below is the crash line
java.lang.IllegalArgumentException: Unable to create converter for java.util.ArrayList<data.india.Delta2>
I call from Globallaunch coroutine where it gets failed
Code :
GlobalScope.launch(Dispatchers.Main) {
val statsRequest = i.getStats()
try {
val response = statsRequest.await()
if(response.){
val statsResponse = response.body() //This is single object Tmdb Movie response
Log.i("stats",""+statsResponse)
}else{
Log.d("MainActivity ",response.errorBody().toString())
}
}catch (e: Exception){
Log.e("Exception",e.localizedMessage)
}
}
You should make the type just List<T>, Moshi only supports the collection interfaces, not the concrete collection classes like ArrayList<T>, LinkedList<T>, etc.. The same goes for other kinds of collections: use Set<T> instead of HashSet<T>, and Map<K, V> instead of HashMap<K, V>, etc..
I don't think the coroutines have anything with the parsing error, try following Reading Json lists using Moshi
Quick snippet will look something like:
// Single item declaration
class SingleListItem(val title: String, val number: Int)
private var listMyData = Types.newParameterizedType(MutableList::class.java, SingleListItem::class.java)
private val adapter: JsonAdapter<List<SingleListItem>> = Moshi.Builder().add(KotlinJsonAdapterFactory()).build().adapter(listMyData)

How can I build correctly the body post in retrofit 2?

I'm trying to post some data with retrofit 2 but I'm gettins some problems... and don't find any example like this...
This is the body that I have to send:
{
"birthday": "12-01-1987",
"name": bob,
"activity": {
"activity_preferences": {
"user_subjects": [4,7,8],
"user_allergies": [1,6,10],
}
}
}
This is my data class:
data class GenericFormDataEntity(
var birthday: String,
var name: String,
#SerializedName("activity")
var food: ActivityEntity?
)
data class ActivityEntity(#SerializedName("activity_preferences")val activityPreferences: ActivityPreferencesEntity)
data class ActivityPreferencesEntity(#SerializedName("user_Subjects")var userSubjects:List<Int>?,#SerializedName("user_allergies")var userAllergies: List<Int>?)
This is the method that I'm trying to build the json:
fun getUserFormEntity(): String{
val paramObject = JSONObject()
paramObject.put("birthday", birthday)
paramObject.put("name", name)
paramObject.put("activity", getActivityEntity())
return paramObject.toString()
}
private fun getActivityEntity(): ActivityEntity{
return ActivityEntity(ActivityPreferencesEntity(selectedSubjectList, selecteAllergiesList))
}
And this is the json that is returning me:
{\"birthday\":\"23-12-2019\",\"name\":Bob,"activity\":\"ActivityEntity(activity_preferences=ActivityPreferencesEntity(user_Subjects=[4,7,8], user_allergies=[1,6,10])"}"
My question is, how can I get the correct json that I have to send as a body:
#Headers("Accept: application/json")
#POST("xxxxxxxx")
suspend fun saveUserData(#Body userFormData: String)
You need to stringify getActivityEntity using Gson.
Gson.toJson(getActivityEntity())
Also, from your API I infer that you are using retrofit why not pass along the entire instance of GenericFormDataEntity as the body for your API.
For enabling this you need to follow by adding GsonConverterFactory.create(gson) to your retrofit.
Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.callFactory(okHttpClient)
.build()

How to add URL parameter in a Retrofit #GET request in Kotlin

I am currently trying to fetch a JSONArray from a server using Retrofit in Kotlin. Here is the interface I am using:
interface TripsService {
#GET("/coordsOfTrip{id}")
fun getTripCoord(
#Header("Authorization") token: String,
#Query("id") id: Int
): Deferred<JSONArray>
companion object{
operator fun invoke(
connectivityInterceptor: ConnectivityInterceptor
):TripsService{
val okHttpClient = OkHttpClient.Builder().addInterceptor(connectivityInterceptor).build()
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://someurl.com/")
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TripsService::class.java)
}
}
}
the desired url is: https://someurl.com/coordsOfTrip?id=201
I am getting the following error message:
retrofit2.HttpException: HTTP 405 Method Not Allowed
I know the URL is working because I can access it via a browser.
Can someone please help me identify what I am doing wrong?
Just change the parameter from
#GET("/coordsOfTrip{id}")
to
#GET("/coordsOfTrip") // remove {id} part that's it
And you'd get the desired URL https://someurl.com/coordsOfTrip?id=201
If you want to use {id} in GET() then you've to use it like below
#GET("/coordsOfTrip{id}")
fun getTripCoord(
#Header("Authorization") token: String,
#Path("id") id: Int // use #Path() instead of #Query()
): Deferred<JSONArray>
But in your case it doesn't require. Follow the first method I mentioned.
For more check Retorfit's official documentation URL Manipulation part
Replace
#GET("/coordsOfTrip{id}")
with:
#GET("/coordsOfTrip?id={id}")

Retrofit2 and Gson, deserialize data inside a certain json element

Inside my app i call some remote APIs that give me a response wrapped into a useless "root" json element. I Paste you an example of json response
{
"response": {
"status": {
//here status fields, common to all responses
},
"configuration": {
//here configurations fields
}
}
}
I'm using an Android studio extension for generate kotlin data classes (JsonToKotlinClass) and i obtain four kotlin classes:
-MyResponseClass, Response, Status, Configuration
where MyResponseClass is like
data class MyResponseClass(
val response: Response
)
there's a way to avoid creation of "Response" class by parsing only it's relative json content and get a MyResponseClass look like like
data class MyResponseClass(
val status:Status,
val configuration: Configuration
)
?
From the title of your question I am assuming that you want to automatically parse the json response to the to the simpler MyResponseClass
You can achieve this by using a type adapter for gson. In your case the adapter class will look similar to the following.
class MyResponseClassAdapter : JsonDeserializer<MyResponseClass> {
override fun deserialize(jsonElement: JsonElement, p1: Type?, p2: JsonDeserializationContext?): MyResponseClass {
val content = jsonElement.asJsonObject["response"]
return Gson().fromJson(content , MyResponseClass::class.java)
}
}
Then add this adapter to a gson instance and use that gson instance with your retrofit instance.
val gson = GsonBuilder()
.registerTypeAdapter(MyResponseClass::class.java, MyResponseClassAdapter ())
.create()
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("{base_url}")
.client(okHttp)
.build()
Yes, when you get the object, instead of converting it straight away, convert it like this
val jsonObject = JSONObject("data")
val innerObject = jsonObject.getJSONObject("status")
and then convert inner object with gson.
This can of course be minified like this:
val object = Gson().fromJson(JSONObject("response").getJSONObject("status"), MyResponseClass::class.java)

Retrofit and Moshi: parsing variable name objects

I'm using Retrofit 2 and Moshi to read and parse JSON from an endpoint. My retrofit instance is defined like so:
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://myendpoint.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create())
.build()
And I'm using the Kotlin data class to store the information in a model:
#GET("data/getlist")
fun getData(): Single<Data>
Data class:
data class Data(val Response : String,
val Message : String,
val BaseImageUrl : String)
Now, because the JSON is formatted like so, the JSON is parsed and the model is populated just fine:
{
"Response": "Success",
"Message": "Api successfully returned",
"BaseImageUrl": "https://www.endpoint.com/image/xxx.jpg",
}
This is because the objects map 1:1 with the model. So in the above example, the "Response" key mapped to the "Response" variable name in the Data class.
My question is this: what if the keys are all variable? How can you represent this in the Kotlin data class?
Sample JSON file to be parsed:
{
"RandomX": "xxxxxx",
"RandomY": "yyyyyy",
"RandomZ": "zzzzzz",
}
As #eric-cochran pointed out, you don't really need a new data class to represent this. It'd end up being a Map, and you'd use it like so:
#GET("data/getlist")
fun getVariableData(): Single<Map<String, String>>

Categories

Resources