Parsing API data which contain object (Klaxon) (Kotlin) - android

I have API response which contain object (graphic)
[
{
"code": 200,
"status": "OK",
"FirstDay": "2019-11-18",
"LastDay": "2019-11-24",
"graphic": {
"2019-11-23": [
{
"godzinaStart": "08:30",
"godzinaStop": "10:00",
"przedmiot": "Matematyka dyskretna",
"sala": "32AK8",
"nauczyciel": "xxx",
"grupy": "1K131; 1K132; 1K133; 1K134; 1K135; 2K131",
"typ": "wykład"
},
],
"2019-11-24": [
{
"godzinaStart": "08:30",
"godzinaStop": "10:00",
"przedmiot": "Podstawy informatyki",
"sala": "308K",
"nauczyciel": "xxx",
"grupy": "1K131",
"typ": "laboratorium"
},
]
}
}
]
I have to parse this JSON to object in Kotlin. So i made class with parameters
class GraphicAPIResponse(
var code: Int,
var status: String,
var errorMessage: String = "",
var FirstDay: String = "",
var LastDay: String = "",
var graphic: JsonObject? = null OR var graphic: JsonArray<Any>? = null (I tried both)
)
I'm parsing data by this function
val responeAPI = Klaxon().parseArray<GraphicAPIResponse>(response)
When graphic is JsonObiect type appliaction throw error
I/System.out: ERROR -> Unable to instantiate JsonObject with parameters []
When graphic is JsonArray<Any> type, here's error
I/System.out: ERROR -> Unable to instantiate GraphicAPIResponse with parameters [LastDay: 2019-11-24, code: 200, status: OK, graphic: java.lang.Object#aef265a, FirstDay: 2019-11-18]
I'm trying to resolve the problem from 2 hours. Can someone help me please? :(
#EDIT
Thank You #Alexey Romanov
That help

Define a type for the nested object:
class Lesson(val godzinaStart: String, val godzinaStop: String, ...)
and use it in GraphicAPIResponse:
class GraphicAPIResponse(
var code: Int,
var status: String,
var errorMessage: String = "",
var FirstDay: String = "",
var LastDay: String = "",
var graphic: Map<String, Lesson> = mapOf()
)
(though honestly, I'd expect JsonObject to work as well)

Related

Kotlin: JSON Schema type "number" gets converted to String when using JSONObject(String)

I implemented a function to validate a given JSONObject against a given JSON Schema JSONObject.
Here is the code:
class JsonSchemaUtilsTest {
//a JSON as a String
val testJsonStr : String =
"""
{
"duration": 28298080890890809809
}
""".trimIndent()
//The JSON schema as a String
val testJsonSchemaStr : String =
"""
{
"${'$'}schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"duration": {
"type": "number",
}
},
"required": [
"duration"
]
}
""".trimIndent()
#Test
public fun test() {
val json : JSONObject = JSONObject(testJsonStr)
val schemaJson : JSONObject = JSONObject(testJsonSchemaStr)
var boolean : Boolean = JsonSchemaUtils.validate(json, schemaJson)
//The following line fails with "org.everit.json.schema.ValidationException: #/duration: expected type: Number, found: String"
Assert.assertTrue("json validates against schemaJson", boolean)
}
}
Im using the https://github.com/everit-org/json-schema library.
So somehow the given Number "duration": 28298080890890809809 is converted to a String when using the JSONObject(String) constructor.
Thanks in Advance

How parse array of arrays JSON with Retrofit?

When I use retrofit, I get JsonSyntaxException : Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3 path $[0] How can I parse it?
[
[
{
"resturan_name": "هتل شاه عباس",
"menu_name": "کباب سلطانی",
"food_name": "پیش غذا"
},
{
"resturan_name": "هتل شاه عباس",
"menu_name": "کباب سلطانی",
"food_name": "پیش غذا"
}
],
[
{
"resturan_name": "هتل شاه عباس",
"menu_name": "کباب سلطانی",
"food_name": "عصرانه"
},
{
"resturan_name": "هتل شاه عباس",
"menu_name": "کباب سلطانی",
"food_name": "عصرانه"
}
]
]
You have an array of array of objects. So, when you're parsing your JSON, you have to use JSONArray:
val jsonArray = JSONArray(your_json)
The json received has a list but you maybe use json object pars in rerofit
see this link for resolve
Parse JSON array response using Retrofit & Gson
Change
Call<...> getListOf....(...);
To
Call<List<...>> getListOf....(...);
Using Response Model
Make your Response Model Class like this Using Gson,
class ResponseModel : ArrayList<ResponseModel.ResponseModelSubList>(){
class ResponseModelSubList : ArrayList<ResponseModelSubList.ResponseModelSubListItem>(){
#Parcelize
data class ResponseModelSubListItem(
#SerializedName("food_name")
val foodName: String? = "",
#SerializedName("menu_name")
val menuName: String? = "",
#SerializedName("resturan_name")
val resturanName: String? = ""
) : Parcelable
}
}
Parse JSON like this,
val response = ResponseModel() // Here response is getting from retofit or other networking lib. you use.
for (i in 0 until response.size) {
val responseList = response[i]
for (j in 0 until responseList.size) {
var foodName = responseList[j].foodName
var menuName = responseList[j].menuName
var restaurantName = responseList[j].resturanName
}
}
Using Manually Parsing
val jsonArray = JSONArray(response)
for (i in 0 until jsonArray.length()){
val jsonArray1 = jsonArray.get(i) as JSONArray
for (j in 0 until jsonArray1.length()){
var jsonObj = jsonArray1.get(j) as JSONObject
var foodName = jsonObj.getString("food_name")
var menuName = jsonObj.getString("menu_name")
var restaurantName = jsonObj.getString("resturan_name")
}
}

how to handle two different Retrofit response in Kotlin?

I have tried to read this similar thread in java in here
so I try the accepted answer there but it doesn't work. here is my problem
I will get two different JSON Response from an endpoint. if I successfully get the Restaurant data, the JSON will be like this
{
"R": {
"has_menu_status": {
"delivery": -1,
"takeaway": -1
},
"res_id": 18941862,
"is_grocery_store": false
},
"id": "18941862",
"name": "Pizza Maru",
"url": "https://www.zomato.com/jakarta/pizza-maru-1-thamrin?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1",
"location": {
"address": "Grand Indonesia Mall, East Mall, Lantai 3A, Jl. M.H. Thamrin No. 1, Thamrin, Jakarta",
"locality": "Grand Indonesia Mall, Thamrin",
"city": "Jakarta",
"city_id": 74,
"latitude": "-6.1955810000",
"longitude": "106.8213770000",
"zipcode": "",
"country_id": 94,
"locality_verbose": "Grand Indonesia Mall, Thamrin, Jakarta"
},
"switch_to_order_menu": 0,
"cuisines": "Pizza",
"timings": "10 AM to 10 PM",
"average_cost_for_two": 180000,
"price_range": 3,
"currency": "IDR",
"thumb": "https://b.zmtcdn.com/data/pictures/chains/2/18941862/403aa36cb046e86a694e7989bb7cd545.jpg?fit=around%7C200%3A200&crop=200%3A200%3B%2A%2C%2A",
"has_online_delivery": 0,
"is_delivering_now": 0,
"store_type": "",
"phone_numbers": "021 3108656",
}
then If I send invalid restaurantID then I will get error JSON Response like this:
{
"code": 404,
"status": "Not Found",
"message": "Not Found"
}
here is the data class I made
data class Restaurant (
#SerializedName("id")
val id : Int = 0,
#SerializedName("name")
var name : String = "",
#SerializedName("url")
val url : String = "",
#SerializedName("location")
val location : Location = Location(),
#SerializedName("currency")
val currency : String = "",
#SerializedName("phone_numbers")
val phone_numbers : String = "",
#SerializedName("thumb")
val thumbnail : String = ""
)
for successful Response
data class Location (
#SerializedName("address")
val address : String = "",
#SerializedName("city")
val city : String = "",
#SerializedName("latitude")
val latitude : Double = 0.0,
#SerializedName("longitude")
val longitude : Double = 0.0,
#SerializedName("zipcode")
val zipcode : String = ""
)
for Error Response
data class ErrorResponse (
val code : Int,
val status : String,
val message : String
)
here is my Interface for my Retrofit. the idea is, I will cast it as Any first, then I will downcast either to Restaurant or ZomatoErrorResponse
interface RestaurantAPI {
#Headers("user-key: $USER_KEY_ZOMATO")
#GET("restaurant")
fun getRestaurantDetail(
#Query("res_id") id: Int
): Call<Any>
}
here is the error:
so I use my retrofit like this
val call = restaurantService.getRestaurantDetail(restaurantID)
call.enqueue(object: Callback<Any>{
override fun onResponse(call: Call<Any>, response: Response<Any>) {
if (response.isSuccessful) {
// this line is executed
Log.d("checkxxx","${response.body()}")
val restaurantData = response.body() as Restaurant // <-- but Error while casting Any to Restaurant in here
restaurant.postValue(restaurantData)
}
}
})
my app crash at that line. but actually I can successfully get the data, but I fail to cast it to Restaurant.
here the logcat of my response.body()
what went wrong in here ?
or maybe there is a better approach than this one
I finally can solve my problem using this code below
val call = restaurantService.getRestaurantDetail(restaurantID)
call.enqueue(object: Callback<Any>{
override fun onResponse(call: Call<Any>, response: Response<Any>) {
if (response.isSuccessful) {
val gson = Gson()
val restaurantData = gson.fromJson(gson.toJson(response.body()), Restaurant::class.java)
} else {
val errorBody = response.errorBody() ?: return
val type = object : TypeToken<ErrorResponse>() {}.type
val errorResponse: ErrorResponse? = gson.fromJson(errorBody.charStream(), type)
val errorMessage = errorResponse?.message ?: "Unknown Error"
}
}
})
don't forget to set the interface to be Any like this
interface RestaurantAPI {
#Headers("user-key: $USER_KEY_ZOMATO")
#GET("restaurant")
fun getRestaurantDetail(
#Query("res_id") id: Int
): Call<Any> // <---- set to Any like this
}
in my case, I have successful response and an error response. so I need to separate it like that.
but if you have 2 successful responses but it has different JSON then you need to perform null checking to restaurantData in my code above, if null then mapping it the other POJO.
You should use gson to convert json to an object
https://github.com/google/gson
Example
val gson = Gson();
val jsonInString = "{\"userId\":\"1\",\"userName\":\"Yasir\"}";
val user = gson.fromJson(jsonInString, User.class);

fetching value of specific key from json response

how can I store a specific value of a key from json response into a variable
{
"results": [
{
"name": ryan,
"roll_id": 64,
"class_id": 310,
"net_id": 95,
},
],
};
above is the json response :-
val gson = GsonBuilder().create()
val ListV = gson.fromJson(body, HomeClass::class.java)
after these 2 lines I'm totally clueless how to do it I've gone through Internet but It was hard for me to understand how to proceed further.
Your Json Structure will be
{
"results": [
{
"name": "Amiyo",
"roll_id": 1,
"class_id": 10,
"net_id": 91
},
{
....
}
]
}
Data class should be
data class HomeClass (
#SerializedName("results") val results : List<Results>
)
data class Results (
#SerializedName("name") val name : String,
#SerializedName("roll_id") val roll_id : Int,
#SerializedName("class_id") val class_id : Int,
#SerializedName("net_id") val net_id : Int
)
fromJson
val listData = gson.fromJson(jsonData, HomeClass::class.java)
Then
val totalSize = 0 until listData!!.size
if(totalSize.size>0)
{
for (i in totalSize)
{
//Your Code i==Position
}
}

Can't convert json response using gson

When I start request using retrofit 2.0, I'm getting this error com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
and don't know why, becoause I have a valid json.
I have also seen this kind of error but didn't helped, I don't want to use JsonReader and this kind of stuff
P.S I'm using Gson for converter
[
{
"fullname": "name",
"contact": "blah",
"uid": "001"
},
{
"fullname": "name2",
"contact": "blah2",
"uid": "002"
}
]
this is my call interface method
#FormUrlEncoded
#POST("url")
fun startRequest(#Field("someField") someField: String) : Call<List<MyModelExample>>
and this is my response MyModelExample class
open class MyModelExample : RealmObject() {
#SerializedName("fullname")
var fullName: String? = null
#SerializedName("contact")
var contact: String? = null
#PrimaryKey
#SerializedName("uid")
var uid: String = ""
}

Categories

Resources