I'm new to Kotlin and I'm still learning how to get an object respose but I'm having the following exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Here's the Json Result:
{"Success":"1","Message":"True","Items":[{"Id":3,"Name":"AndroidVersion","Value":"1"},{"Id":4,"Name":"IOSVersion","Value":"1.0"},{"Id":5,"Name":"AndroidForceUpdate","Value":"true"}]}
Here's the model class
data class MobileConfiguration(
val id: Int,
val name: String,
val value: String
) {
class Deserializer : ResponseDeserializable<Array<MobileConfiguration>> {
override fun deserialize(content: String): Array<MobileConfiguration>? = Gson().fromJson(content, Array<MobileConfiguration>::class.java)
}
}
And here's the Call:
url.httpPost(listOf(Pair("key", ""))).responseObject(MobileConfiguration.Deserializer()) { request, response, result ->
//val (people, err) = result.get()
when (result) {
is Result.Failure -> {
val ex = result.getException()
Log.wtf("ex", "is " + ex)
}
is Result.Success -> {
val (config, err) = result
//Add to ArrayList
config?.forEach { configuration ->
mobileConfigurations.add(configuration)
}
println(mobileConfigurations)
}
}
}
What Am i doing wrong please if anybody can help
I think the problem is in the model class.
You assume that the MobileConfiguration array is directly under the response class, however, actually, it is under the Items object.
So instead of using Array<MobileConfiguration> as a responseObject, you should use other class as a container like:
data class MobileConfigurationResponse(
val success: String,
val message: String,
val items: Array<MobileConfiguration>
)
and then you can get the array like this:
val (config, err) = result.items
Related
I am making generic classes for hitting otp api.anybody can use otp section just have to pass request ,Response class and url and all will be done by this otp section.
Please note : this response class can be of different type (for eg: MobileOtpResponse,EmailOtpResponse)
below is the generic OtpClient which takes any request type and returns particular passed ResponseType (for example : Request class passed is OtpRequest ,ResponseType class passed is OtpResponse)
interface OtpClient {
#POST
suspend fun <Request : Any, ResponseType> sendOtp(#Url url: String,
#Body request:#JvmSuppressWildcards Any): #JvmSuppressWildcards ResponseType
}
OtpRequest
data class OtpRequest(#SerializedName("mobile_number") val mobileNumber: String,#SerializedName("app_version") val appVersion: String)
OtpResponse
data class OtpResponse(#SerializedName("status") val status: String = "",
#SerializedName("response") val response: OtpData? = null)
data class OtpData(
#SerializedName("otp_status") val otpStatus: Boolean = false,
#SerializedName("message") val message: String = "",
#SerializedName("error") val error: Int? = null,
#SerializedName("otp_length") val otpLength: Int? = null,
#SerializedName("retry_left") val retryLeft: Int? = null,)
Now i create Repo to call this api this simply use flow and when the data fetch it emits the data
class OtpRepoImpl<out Client : OtpClient>(val client: Client) :OtpRepo {
override fun <Request:Any, ResponseType> sentOtpApi(url: String, request: Request): Flow<ResponseType> {
return flow<ResponseType> {
// exectute API call and map to UI object
val otpResponse = client.sendOtp<Request, ResponseType>(url,request)
emit(otpResponse)
}.flowOn(Dispatchers.IO) // Use the IO thread for this Flow
}
}
this repo is used in viewmodel class
#ExperimentalCoroutinesApi
fun <A : Class<ResponseType>, Request : Any, ResponseType : Any> sendOtp(a: Class<ResponseType>, request: Request, response: ResponseType, url: String) {
viewModelScope.launch {
repo.sentOtpApi<Request, ResponseType>(url, request = request)
.onStart { _uiState.value = OtpState.Loading(true) }
.catch { cause ->
_uiState.value = OtpState.Loading(false)
getResponseFromError<Class<ResponseType>,ResponseType>(cause, response) {
// emit(it)
}
}
.collect {
_uiState.value = OtpState.Loading(false)
_uiState.value = OtpState.Success(it)
}
}
}
as you can see above this sendOtp method is called from the view class and inside this method we use repo.sentOtpApi and pass generic request response type.I get data in catch block coz api is send error otp data in 400 HttpException so i created another method getResponseFromError to get error response it should parse the errorBody response and call this lambda block.
private suspend fun <A : Class<*>, ResponseType : Any> getResponseFromError( cause: Throwable, rp: ResponseType, block: suspend (ResponseType) -> Unit) {
if (cause is HttpException) {
val response = cause.response()
if (response?.code() == 400) {
println("fetching error Response")
val errorResponse = response.errorBody()?.charStream()
val turnsType = object : TypeToken<ResponseType>() {}.type
val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
block(finalErrorResponse)
} else {
println("someOther exception")
}
} else
_uiState.value = OtpState.Error(cause)
}
so here i am facing the problem inside above method
val turnsType = object : TypeToken<ResponseType>() {}.type
val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
block(finalErrorResponse)
This finalErrorResponse is returning LinkedTreeMap instead of ResponseType (in this case its OtpResponse)
i have also tried using Class<*> type like this
val turnsType = object : TypeToken<A>() {}.type
val finalErrorResponse = Gson().fromJson<A>(errorResponse, turnsType)
but its not working.
calling of this sentOtp viewmodel func is like
var classType = OtpResponse::class.java
otpViewModel.sendOtp(a = classType, request = otpRequest, response = OtpResponse() , url =
"http://preprod-api.nykaa.com/user/otp/v2/send-wallet-otp")
[![value in finalErroResponse][1]][1]
[1]: https://i.stack.imgur.com/Holui.png
required: finalErroResponse should be of OtpResponse type because that was passed in sentOtp func
Please help :)
I'm using mvvm , kotlin , retrofit and courtin in my app . I've done several request and all of them works fine but with this one , I get this error "Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter list"
this is my json
{
"roomslist": [
{
"id": "1"
}
]
}
these are my models
data class RoomsListModel(
#Json(name = "roomslist")
val roomsList: List<Rooms>
)
data class Rooms(
#Json(name = "id")
val id: String
}
this is my api interface :
#FormUrlEncoded
#POST("getPlaceRooms.php")
fun getPlaceRooms2(#Field("amlakid")id:String):Deferred<RoomsListModel>
this is my repository :
fun getRooms(
amlakId: String
): MutableLiveData<RoomsListModel> {
scope.launch {
val request = api.getPlaceRooms2(amlakId)
withContext(Dispatchers.Main) {
try {
val response = request.await()
roomsLiveData.value = response
} catch (e: HttpException) {
Log.v("this", e.message);
} catch (e: Throwable) {
Log.v("this", e.message);
}
}
}
return roomsLiveData;
}
when the app runs , it goes into e: Throwable and returns the error
my viewmodel
class PlacesDetailsViewModel : ViewModel() {
private val repository = PlaceDetailsRepository()
fun getRooms(amlakId: String):MutableLiveData<RoomsListModel> {
return repository.getRooms(amlakId)
}
}
and this my activity request :
viewModel.getRooms(amlakId).observe(this, Observer {
vf.frm_loading.visibility = View.GONE
it?.let {
adapter.updateList(it?.roomsList)
setNoItem(false)
}
})
I'm using moshi
I've tried to clean ,rebuild but it doesn't make any different
could you help me ?
what is going wrong with my code?
You should try adding ? to your Model parameters. Not sure if in your case is the String?. It will ensure that you can have null values on your String
val id: String?
Please double check, whatever value is missing or null in your case
Have you tried removing #Json annotation in your val id: String declaration?
I'm new to kotlin so this maybe a very easy issue to resolve.
What I'm trying to do is filter the json response that I receive using Retrofit2 before I display the images in a grid with a RecyclerView.
instagram.com/explore/tags/{hashtag}/?__a=1&max_id= Using Retrofit2 I'm able to get the data response fine and also display the given url images in a RecyclerView.
I have not been successful in using the filter, map, loops and conditions to remove elements from the Arraylist. I do not understand these to the fullest extent but I have searched looking for solutions and those are what I came apon.
Interface
interface InstagramDataFetcher
{
#GET("tags/{tag}/?__a=1&max_id=")
fun getInstagramData(#Path("tag") hashtag: String) : Call <InstagramResponse>
}
Where I get my response from and also get StringIndexOutOfBoundsException
class InstagramFeedFragment : Fragment()
{
private fun onResponse()
{
val service = RestAPI.retrofitInstance?.create(InstagramDataFetcher::class.java)
val call = service?.getInstagramData("hashtag")
call?.enqueue(object : Callback<InstagramResponse>
{
override fun onFailure(call: Call<InstagramResponse>, t: Throwable)
{
Log.d("FEED", " $t")
}
override fun onResponse(
call: Call<InstagramResponse>, response: Response<InstagramResponse>
)
{
//for ((index, value) in data.withIndex())
if (response.isSuccessful)
{
var data: ArrayList<InstagramResponse.InstagramEdgesResponse>? = null
val body = response.body()
data = body!!.graphql.hashtag.edge_hashtag_to_media.edges
for ((index, value) in data.withIndex())
{
if(value.node.accessibility_caption[index].toString().contains("text") ||
value.node.accessibility_caption[index].toString().contains("person"))
{
data.drop(index)
}
}
recyclerView.adapter = InstagramGridAdapter(data, parentFragment!!.context!!)
}
}
})
}
}
This is my model class
data class InstagramResponse(val graphql: InstagramGraphqlResponse)
{
data class InstagramGraphqlResponse(val hashtag: InstagramHashtagResponse)
data class InstagramHashtagResponse(val edge_hashtag_to_media: InstagramHashtagToMediaResponse)
data class InstagramHashtagToMediaResponse(
val page_info: InstagramPageInfo,
val edges: ArrayList<InstagramEdgesResponse>
)
data class InstagramPageInfo(
val has_next_page: Boolean,
val end_cursor: String
)
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val __typename: String,
val shortcode: String,
val display_url: String,
val thumbnail_src: String,
val thumbnail_resources: ArrayList<InstagramThumbnailResourceResponse>,
val is_video: Boolean,
val accessibility_caption: String
)
data class InstagramThumbnailResourceResponse(
val src: String,
val config_width: Int,
val config_height: Int
)
}
Simply again, I want to just remove elements from the arraylist that match certain things what I don't want. For instance. the "is_video" value that comes from the json. I want to go through the arraylist and remove all elements that have "is_video" as true.
Thanks
If you asking how to filter the list then below is the demo.
You just need to use filter on your data which is an ArrayList. I've tried keeping the same structure for the models so that you can get a better understanding.
fun main() {
val first = InstagramNodeResponse(
title = "first",
is_video = true
)
val second = InstagramNodeResponse(
title = "second",
is_video = false
)
val list: ArrayList<InstagramEdgesResponse> = arrayListOf(
InstagramEdgesResponse(node = first),
InstagramEdgesResponse(node = second)
)
val itemsWithVideo = list.filter { it.node.is_video == true }
val itemsWithoutVideo = list.filter { it.node.is_video == false }
println(itemsWithVideo.map { it.node.title }) // [first]
println(itemsWithoutVideo.map { it.node.title }) // [second]
}
// Models
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val title: String,
val is_video: Boolean
)
Following is my request parameters in PostMan
{"assign_id":"1","type":2,"attendance_list":[{"stud_id":"1703","attendanceID":"1","stud_attendance":"4"},{"stud_id":"1704","attendanceID":"2","stud_attendance":"1"},{"stud_id":"1705","attendanceID":"3","stud_attendance":"1"},{"stud_id":"1706","attendanceID":"4","stud_attendance":"1"},{"stud_id":"1707","attendanceID":"5","stud_attendance":"1"},{"stud_id":"1727","attendanceID":"25","stud_attendance":"1"}]}
Following is the response
{"status":1,"msg":"Success"}
Now in my Android App I am using Retrofit with Gson. But passing through Gson, I was facing some problem so I am sending request parameters in form of jsonObject and jsonArrays.
Following is my code when a button is pressed to submit request to server
val jObjRequest = JsonObject()
jObjRequest.addProperty("assign_id",ClassModelInstance.getInstance().classInfo.assignId)
jObjRequest.addProperty("type","2")
val attendanceArray = JsonArray()
for(i in 0 until ClassModelInstance.getInstance().studentInfos.size){
val jsonObject = JsonObject()
jsonObject.addProperty("stud_id",ClassModelInstance.getInstance().studentInfos[i].studId)
jsonObject.addProperty("attendanceID",1)
jsonObject.addProperty("stud_attendance",ClassModelInstance.getInstance().studentInfos[i].studAttendance)
attendanceArray.add(jsonObject)
}
jObjRequest.addProperty("attendance_list",attendanceArray.toString())
Log.i("PritishAttendanceApi2", jObjRequest.toString())
val submitAttendanceInterface = ApiClient.client.create(SubmitAttendanceInterface::class.java)
submitAttendanceInterface.takeAttendance(jObjRequest)
.enqueue(object : Callback<SubmitAttendanceResponse> {
override fun onFailure(call: Call<SubmitAttendanceResponse>, t: Throwable) {
activity?.let { it1 -> ToastMaker.make(it1,getString(R.string.something_went_wrong),Toast.LENGTH_LONG) }
Log.i("Pritish",t.message+"\t"+t.localizedMessage+"\t"+t.printStackTrace()+"\t"+t.cause+"\n"+call.request())
alertDialog.dismiss()
}
override fun onResponse(call: Call<SubmitAttendanceResponse>, response: Response<SubmitAttendanceResponse>) {
if(response.body()?.status.toString().equals("1",true)){
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
goToPreviousFragment()
} else {
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
}
alertDialog.dismiss()
}
})
This is the interface and response class
interface SubmitAttendanceInterface {
#Headers("Content-Type: application/json")
#POST("timetable/takeAttendance")
fun takeAttendance(#Body body: JsonObject): Call<SubmitAttendanceResponse>
}
data class SubmitAttendanceResponse(
#SerializedName("status")
#Expose
var status: Int? = null,
#SerializedName("msg")
#Expose
var msg: String? = null
)
When I log using HttpInterceptor I get com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 1 path
I searched Stack Overflow for the above error but the answers didn't met my requirement
JSON Error "java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"
"Expected BEGIN_OBJECT but was STRING at line 1 column 1"
I have edited the url in the logs as I don't want to expose the URL.
as per your log and sample data you should post data "attendance_list" as an json array insted of string
try
jObjRequest.add("attendance_list",attendanceArray)
insted of
jObjRequest.addProperty("attendance_list",attendanceArray.toString())
Facing Problem on getting Response in ArrayList.
I have following Respose on String value
var res_message: String = ""
res_message = "${result.vehicletypes} "
Getting below Value on this String
[VehicleType(_id=1, vehicleType=Hatchback, __v=0),
VehicleType(_id=2, vehicleType=Maruti, __v=0),
VehicleType(_id=3, vehicleType=Honda, __v=0),
VehicleType(_id=4, vehicleType=Bike, __v=0)]
Retrofit Result is
vehicletypes = {ArrayList#6055} size = 4
0 = {Model$VehicleType#6058} "VehicleType(_id=1,
vehicleType=Hatchback, __v=0)"
1 = {Model$VehicleType#6059} "VehicleType(_id=2,
vehicleType=Maruti, __v=0)"
2 = {Model$VehicleType#6060} "VehicleType(_id=3,
vehicleType=Honda, __v=0)"
3 = {Model$VehicleType#6061} "VehicleType(_id=4,
vehicleType=Bike, __v=0)"
Below Code snippest sending request to API.
disposable = apiServices.getVehicle(token)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
res_message = "${result.vehicletypes} "
Log.d("Type==", res_message)
},
{ error ->
res_message = "${error.message}"
// validateToken()
}
)
Model Class
data class Vehicles(val success: Boolean, val vehicletypes: List<VehicleType>, val message: String)
data class VehicleType(val _id: String, val vehicleType: String, val __v: String)
I want to get this value on Arralist VehicleType List on below vehicleListArray
private var vehicleListArray: ArrayList<Model.VehicleType>? = null
How we can achieve this.
Thanks in advance.
Assuming that what you are trying to parse is a response from a service that is able to send you propper format for lists (eg Json) than
Retrofit can handle parsing lists with ease.
In your apiService definition:
fun getPeople(token: Whatever): Observable<List<VehicleType>>
And if you don't have it already:
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
I got solution I have to handle Respose as below code snippet.
private fun getVehicleType() {
disposable?.add(apiServices.getVehicle(token)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError))
}
private fun handleResponse(vehicles: Model.Vehicles) {
VehiclesArrayList = ArrayList(vehicles.vehicletypes)
Log.d("type==","n--"+VehiclesArrayList )
mAdapter = DataAdapter(VehiclesArrayList !!, this)
v_android_list.adapter = mAdapter
}
private fun handleError(error: Throwable) {
Log.d("type", error.localizedMessage)
Toast.makeText(context, "Error ${error.localizedMessage}", Toast.LENGTH_SHORT).show()
}