retrofit - kotlin - Parameter specified as non-null is null - android

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?

Related

I keep getting the error "E/Network: searchBooks: Failed Getting books" but I am not sure why

So I am using the Google's API and for some reason, I'm getting a generic error:
E/Network: searchBooks: Failed Getting books
When it initially loads up, the hard coded query "android" shows up with a list of books associated with the book topic. But when I search up a different topic like "shoes" for example, the error shows up. Even when you hard code a different topic other than "android", it still shows the error. I have checked the API and it is working properly with the different query searches.
Here's the Retrofit Interface:
#Singleton
interface BooksApi {
#GET(BOOK_EP)
suspend fun getAllBooks(
//don't initialize the query, so that the whole api is available to the user
#Query("q") query: String
): Book
#GET("$BOOK_EP/{bookId}")
suspend fun getBookInfo(
#Path("bookId") bookId: String
): Item
}
The Repo
class BookRepository #Inject constructor(private val api: BooksApi) {
suspend fun getBooks(searchQuery: String): Resource<List<Item>> {
return try {
Resource.Loading(data = true)
val itemList = api.getAllBooks(searchQuery).items
if(itemList.isNotEmpty()) Resource.Loading(data = false)
Resource.Success(data = itemList)
}catch (exception: Exception){
Resource.Error(message = exception.message.toString())
}
}
suspend fun getBookInfo(bookId: String): Resource<Item>{
val response = try {
Resource.Loading(data = true)
api.getBookInfo(bookId)
}catch (exception: Exception){
return Resource.Error(message = "An error occurred ${exception.message.toString()}")
}
Resource.Loading(data = false)
return Resource.Success(data = response)
}
The ViewModel:
class SearchViewModel #Inject constructor(private val repository: BookRepository): ViewModel(){
var list: List<Item> by mutableStateOf(listOf())
var isLoading: Boolean by mutableStateOf(true)
init {
loadBooks()
}
private fun loadBooks() {
searchBooks("android")
}
fun searchBooks(query: String) {
viewModelScope.launch(Dispatchers.Default) {
if (query.isEmpty()){
return#launch
}
try {
when(val response = repository.getBooks(query)){
is Resource.Success -> {
list = response.data!!
if (list.isNotEmpty()) isLoading = false
}
is Resource.Error -> {
isLoading = false
Log.e("Network", "searchBooks: Failed Getting books", )
}
else -> {isLoading = false}
}
}catch (exception: Exception){
isLoading = false
Log.d("Network", "searchBooks: ${exception.message.toString()}")
}
}
}
}
I'll leave the project public so you guys can check it out for more of an understanding
Github Link: https://github.com/OEThe11/ReadersApp
P.S. you would have to create a login (takes 30 sec), but once you do, you'll have access to the app immediately.
This issue is occurring because of JsonSyntaxException java.lang.NumberFormatException while the JSON response is getting parsed from the API. This is because the averageRating field in the VolumeInfo data class is declared as Int but the response can contain floating point values.
If you change averageRating field type from Int to Double in the VolumeInfo data class, the exception would no longer occur.
I suggest you to debug your code in such cases.

Type mismatch: inferred type is Unit when Weather was expected

I am working on an Android Weather application and I am getting the error described in the title. The function that is causing this error is a repository that is calling some weather data. I have a helper class called DataOrException which is:
class DataOrException<T, Boolean, E>(
var data: T? = null,
var loading: Kotlin.Boolean? = null,
var e: E? = null
)
The function that is calling this class is a coroutine that is getting the weather information from repository which is using Injection to return the class. Here's the function:
suspend fun getWeather(cityQuery: String, units: String): DataOrException<Weather, Boolean, Exception> {
val response = try {
api.getWeather(query = cityQuery, units = units)
} catch (e: Exception) {
Log.d("REX", "getWeather: $e")
return DataOrException(e = e)
}
return DataOrException(data = response) //Error occurs here.
Any ideas on how to fix this error?
Your getWeather function in WeatherApi is not returning anything so it's basically a kotlin Unit. But, In your repository return DataOrException(data = response) here data is expected to be of type Weather. That's why the error.
Solution:
Return Weather from WeatherApi function getWeather & keep everything else as it was.
interface WeatherApi {
#GET(value = "/cities/cityID=Chelsea")
suspend fun getWeather(#Query("q") query: String, #Query("units") units: String = "imperial") : Weather
}
OR
================
Change data type to Unit by changing to : DataOrException<Unit, Boolean, Exception>
suspend fun getWeather(
cityQuery: String,
units: String
): DataOrException<Unit, Boolean, Exception> {
val response = try {
api.getWeather(query = cityQuery, units = units)
} catch (e: Exception) {
Log.d("REX", "getWeather: $e")
return DataOrException(e = e)
}
return DataOrException(data = response)
}

How to make Retrofit deserialize response to null object when the returned type is not matching the expected?

Retrofit is not deserialising correctly. Because I don't have control over backend I need my code to adapt to it, I need Retrofit to deserialize response to null when it isn't the right type. Instead it creates the object but with null value members. So instead of returning null it returns DefaultResponseResult(success=null, action=null, res=null)
This is my method (I tried fixing it with ? at Call<DefaultResponseResult?>):
#PUT("/SOMETHING/fcmtoken/")
fun registerFCMToken(#Query("fcmToken") fcmToken: String, #Query("driverID") driverID: String, #Query("dtoken") dtoken: String): Call<DefaultResponseResult?>
and in client file:
fun <T> fetch(requestCall: Call<T?>, completion: ((Result<T, Exception>) -> Unit)) {
requestCall.enqueue(object: Callback<T?> {
override fun onResponse(call: Call<T?>, response: Response<T?>) {
val responseData = response.body()
var errorLog: ErrorLog? = response.errorBody()?.let { gson.fromJson(it.charStream(), ErrorLog::class.java) }
//FOR SOME REASON RESPONSE DATA IS NOT NULL BUT IT SHOULD BE
if (responseData == null) {
completion(Failure(APIError(internalError = "Unknown")))
return
}
//THIS PRINTS DefaultResponseResult(success=null, action=null, res=null) unfortunately
printLogs(responseData.toString())
completion(Success(responseData))
}
override fun onFailure(call: Call<T?>, t: Throwable) {
printLogs("Failure")
printLogs(call.request().toString())
printLogs(t.toString())
completion(Failure(APIError(internalError = "Failure")))
}
})
}
DefaultResponseResult:
data class DefaultResponseResult(
val success: String?,
val action: String?,
val res: String?
)
ErrorLog:
data class ErrorLog(
val error: ErrorLogBody
)
data class ErrorLogBody(
val authentication: String? = null
//...
)
Request returns raw response as:
{
"error": "SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
'ecyNabqKQjSPx-zn6Oshpd:APA91bFS7fGpnmnO_CiYfkQoF0Oo52ODWGRt-M...' for
key 'PRIMARY'" }
And retrofit doesn't recognize it as error body because http code is 200 and that's something I can't change in the backend. I expect it to cast response.body() to null because data type is ErrorLog not DefaultResponseResult, instead it does this (from logcat console):
DefaultResponseResult(success=null, action=null, res=null)
How can I make it return null instead of DefaultResponseResult?

Android generic Gson.fromJson() conversion with coroutine and flow

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 :)

Fuel Expected Begin Array but was Begin Object

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

Categories

Resources