Save JSON object from response body. Retrofit2 - android

This is the response.body I'm receiving
D/OkHttp: <-- 201 Created http://192.168.0.2:8000/api/surveys (962ms)
D/OkHttp: Host: 192.168.0.2:8000
D/OkHttp: Date: Thu, 23 Apr 2020 15:25:40 GMT
D/OkHttp: Connection: close
D/OkHttp: X-Powered-By: PHP/7.3.10
D/OkHttp: Cache-Control: no-cache, private
D/OkHttp: Date: Thu, 23 Apr 2020 15:25:40 GMT
D/OkHttp: Content-Type: application/json
D/OkHttp: X-RateLimit-Limit: 60
D/OkHttp: X-RateLimit-Remaining: 59
//this is what I want to save
D/OkHttp: [{"id":2,"evaluation_id":1,"user_id":null,"group_id":1,"created_at":"2020-04-20 11:04:31",
"updated_at":"2020-04-20 11:04:31","answered":false,"evaluation":{"id":1,"survey_id":1,
"init_date":"2020-04-13 12:00:00","end_date":"2020-04-30 12:00:00","report":0,"report_date":null,
"report_end":null,"created_at":"2020-04-20 11:04:21","updated_at":"2020-04-20 11:04:21",
"survey":{"id":1,"name":"asdfasdfds group","description":"<p>adfadsfa<strong><em>dsf<\/em>
<\/strong>asdfadsf asd asdf f sad fsd<\/p>","user_id":1,"anonymous":0,"created_at":"2020-04-20 11:04:21",
"updated_at":"2020-04-20 11:04:31"}}},{"id":5,"evaluation_id":2,"user_id":1,"group_id":null,
"created_at":"2020-04-21 11:00:19","updated_at":"2020-04-21 11:00:19","answered":true,
"evaluation":{"id":2,"survey_id":2,"init_date":"2020-04-13 12:00:00","end_date":"2020-04-24 12:00:00","report":0,
"report_date":null,"report_end":null,"created_at":"2020-04-20 11:05:43","updated_at":"2020-04-20 11:05:43",
"survey":{"id":2,"name":"asdfa single","description":"<p>asdfa<\/p>","user_id":1,"anonymous":1,
"created_at":"2020-04-20 11:05:43","updated_at":"2020-04-21 11:00:19"}}}]
D/OkHttp: <-- END HTTP (1123-byte body)
I have created the POJO models for the objects inside the main object, this is a retrofit response so I have it like this for now
#Override
public void onResponse(Call<Survey> call, Response<Survey> response) {
if (!response.isSuccessful()) {
FancyToast.makeText(activity, getString(R.string.error),
FancyToast.LENGTH_SHORT, FancyToast.CONFUSING,
R.drawable.error_outline, false).show();
return;
}
System.out.println("response: " + response.body());
}
How do I get the JSON from the response body to then save in the models?

Here is how your model class should be:
class Model : ArrayList<ModelItem>(){
data class ModelItem(
val answered: Boolean = false,
val created_at: String = "",
val evaluation: Evaluation = Evaluation(),
val evaluation_id: Int = 0,
val group_id: Int = 0,
val id: Int = 0,
val updated_at: String = "",
val user_id: Int = 0
) {
data class Evaluation(
val created_at: String = "",
val end_date: String = "",
val id: Int = 0,
val init_date: String = "",
val report: Int = 0,
val report_date: Any = Any(),
val report_end: Any = Any(),
val survey: Survey = Survey(),
val survey_id: Int = 0,
val updated_at: String = ""
) {
data class Survey(
val anonymous: Int = 0,
val created_at: String = "",
val description: String = "",
val id: Int = 0,
val name: String = "",
val updated_at: String = "",
val user_id: Int = 0
)
}
}
}
Make sure your are using GSON converter library in your retrofit.

Related

Retrofit - multipart

I would like to upload image to api. I have headers for request and also headers for Part with files:
#Multipart
#POST("/api")
suspend fun uploadFile(
#Part("payload") file: RequestBody,
#Header("Mobile-Client-Type") mobileClientType: String,
#Header("Mobile-Client-Version") version: String,
#Header("Authorization") authorization: String,
): retrofit2.Response<Any>
and
internal class RetrofitFileUpload {
private val fileUploadService = FileUploadService.create()
suspend fun requestWithPayload(
urlString: String,
file: File,
myHeaders:
pl.probs.uploadapp.jsonObjects.Headers,
payload: Payload
): retrofit2.Response<Any> {
val mediaType = "multipart/form-data".toMediaTypeOrNull()
val fileBody = RequestBody.create(mediaType, file)
val body: RequestBody = MultipartBody.Builder().addPart(
Headers.headersOf(
"type",
payload.type,
"identifier",
payload.identifier,
"omit_package_creation",
payload.omitPackageCreation.toString(),
"file_path",
payload.filePath.toString()
), fileBody
).build()
return fileUploadService.uploadFile(
body,
myHeaders.mobileClientType,
myHeaders.mobileClientVersion,
myHeaders.authorization,
)
}
}
Result looks like I have 2 parts instead of one:
Content-Type: multipart/form-data; boundary=eac4d249-196d-421e-9ffe-923b472a5f58
....other headers...........
--eac4d249-196d-421e-9ffe-923b472a5f58
Content-Disposition: form-data; name="payload"
Content-Transfer-Encoding: binary
Content-Type: multipart/mixed; boundary=55d4b971-d171-42a7-a82a-5deb1bccac98
Content-Length: 2421
--55d4b971-d171-42a7-a82a-5deb1bccac98
type: myType
identifier: myID
omit_package_creation: true
file_path: /data/0...
Content-Type: multipart/form-data
Content-Length: 2072
{[Content from file]}
--55d4b971-d171-42a7-a82a-5deb1bccac98--
WHat should I change to have only 1 part?

Android retrofit: 400 bad request response code

So I'm still in the process of learning android dev and I'm currently working on an app which is supposed to show students their grades. Right now I am stuck at getting login to a service from which grades are collected. For that process I am using https://eduo-ocjene-docs.vercel.app/ api (documentation is in Croatian).
This is what curl request for logging in looks like:
curl --location --request GET 'https://ocjene.eduo.help/api/login' \--header 'Content-Type: application/json' \--data-raw '{ "username":"ivan.horvat#skole.hr", "password":"ivanovPassword123"}'
Here are screenshots of what I have tried until now
Here is how I build retrofit
object ApiModule {
private const val BASE_URL = "https://ocjene.eduo.help/"
lateinit var retrofit: EdnevnikApiService
private val json = Json { ignoreUnknownKeys = true }
fun initRetrofit() {
val okhttp = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}).build()
retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.client(okhttp).build().create(EdnevnikApiService::class.java)
}
}
The login method
interface EdnevnikApiService {
#HTTP(method = "get", path = "/api/login", hasBody = true)
fun login(#Body request: LoginRequest): Call<LoginResponse>
}
This is what happens when the login button is clicked
fun onLoginButtonClicked(email: String, password: String) {
val request = LoginRequest(email, password)
ApiModule.retrofit.login(request).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
loginResultLiveData.value = response.isSuccessful
val body = response.body()
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
loginResultLiveData.value = false
}
})
}
and this is what kotlin request and kotlin response data classes look like
#kotlinx.serialization.Serializable
data class LoginRequest(
#SerialName("username") val username: String,
#SerialName("password") val password: String,
)
#kotlinx.serialization.Serializable
data class LoginResponse(
#SerialName("LoginSuccessful") val isSuccessful: Boolean,
)
Oh and this is what I get from the interceptor when I send the request
My guess is server is responding with 400 Bad Request due to unsupported method type. When I replaced method = "get" with method = "GET" in your sample code, I received:
java.lang.IllegalArgumentException: method GET must not have a request body.
which makes sense. Luckily, the /login API you shared works with POST method type, so you can try using:
#HTTP(method = "POST", path = "/api/login", hasBody = true,)
I checked at my end and I received the following response:
<-- 200 https://ocjene.eduo.help/api/login (1390ms)
access-control-allow-origin: *
access-control-allow-credentials: true
set-cookie: epicCookie=f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a;
content-type: application/json; charset=utf-8
etag: "bkrbkvg0eo6c"
vary: Accept-Encoding
date: Thu, 10 Nov 2022 03:07:08 GMT
server: Fly/b1863e2e7 (2022-11-09)
via: 2 fly.io
fly-request-id: 01GHFR2T56X9K0GFN3DH1Z9JYV-sin
{"LoginSuccessful":false,"token":"f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a"}
<-- END HTTP (228-byte body)
object ApiModule {
private const val BASE_URL = "https://ocjene.eduo.help/"
lateinit var retrofit: EdnevnikApiService
private val json = Json { ignoreUnknownKeys = true }
fun initRetrofit() {
val okhttp = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}).build()
retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.client(okhttp).build().create(EdnevnikApiService::class.java)
}
}

send get request to open_elevation.com with kotlin and retrofit

I want to get altitude from api service and:
base url is https://api.open-elevation.com/
end point is api/v1/lookup?
and get two args lat and long like this
https://api.open-elevation.com/api/v1/lookup?locations=42,35
output is a json
How can i send get request and get data and please help
Hm. unfortunately you can't send a request with varargs through retrofit. But, you can do it like this.
Do something like this..
lifecycleScope.launch {
val latLong = arrayOf(41.161758, -8.583933)
val result = withContext(Dispatchers.IO) {
RetroClient.getInstance().lookup(latLong.joinToString())
}.awaitResponse()
if (result.isSuccessful && result.code() == 200) {
// If success do your sutff here
}
}
RetroClient
object RetroClient {
private val httpInterceptor by lazy {
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
}
private val client by lazy {
OkHttpClient.Builder()
.addInterceptor(httpInterceptor)
.callTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.build()
}
private val gson by lazy {
GsonBuilder().setLenient().create()
}
fun getInstance(): RetroInterface {
return Retrofit.Builder()
.baseUrl("https://api.open-elevation.com/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson)).build()
.create(RetroInterface::class.java)
}
}
RetroInterface
interface RetroInterface {
#GET("lookup")
fun lookup(
#Query("locations") locations: String,
): Call<LocationsModel?>
}
LocationsModel
data class LocationsModel(
var results: List<Result>?
)
data class Result(
var elevation: Int?,
var latitude: Double?,
var longitude: Double?
)
RESULTS
I/okhttp.OkHttpClient: <-- 200 OK https://api.open-elevation.com/api/v1/lookup?locations=41.161758%2C%20-8.583933 (1411ms)
I/okhttp.OkHttpClient: Server: nginx/1.21.1
I/okhttp.OkHttpClient: Date: Sat, 27 Aug 2022 18:33:55 GMT
I/okhttp.OkHttpClient: Content-Type: application/json
I/okhttp.OkHttpClient: Transfer-Encoding: chunked
I/okhttp.OkHttpClient: Connection: keep-alive
I/okhttp.OkHttpClient: Access-Control-Allow-Origin: *
I/okhttp.OkHttpClient: Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS
I/okhttp.OkHttpClient: Access-Control-Allow-Headers: Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token
I/okhttp.OkHttpClient: Strict-Transport-Security: max-age=31536000; includeSubDomains
I/okhttp.OkHttpClient: {"results": [{"latitude": 41.161758, "longitude": -8.583933, "elevation": 117}]}
I/okhttp.OkHttpClient: <-- END HTTP (80-byte body)
Hope you understand. - Thank you.

Use JsonReader.setLenient(true) to accept malformed JSON in kolin using retrofit

I am using retrofit2 for API calls. When it called to my API it shows below error;
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
#Step1: Here is my JONSON
[
{
"branchCode": "053",
"logitude": " ",
"phone": " +8809613225301",
"latitude": " ",
"sl": "1",
"branchName": "A K KHAN MOOR BRANCH",
"branchAddress": "The Downing(1st Floor), H-825,, Zakir Hossain Road, A K Khan,",
"opendate": " ",
"fax": " n/a"
},
{
"branchCode": "058",
"logitude": " ",
"phone": " ",
"latitude": " ",
"sl": "2",
"branchName": "AGANAGAR BRANCH",
"branchAddress": "Aganagar Branch, Shawon Plaza (1st Floor)",
"opendate": " ",
"fax": " "
}
]
#2Step: here is my data class
data class LocationdataModel(
#SerializedName("sl")
var sl: String = "",
#SerializedName("atmCode")
var atmCode: String = "",
#SerializedName("atmLocation")
var atmLocation: String = "",
#SerializedName("atmLogitude")
var atmLogitude: String = "",
#SerializedName("atmLatitued")
var atmLatitued: String = "",
#SerializedName("branchCode")
var branchCode: String = "",
#SerializedName("branchName")
var branchName: String = "",
#SerializedName("branchAddress")
var branchAddress: String = "",
#SerializedName("phone")
var phone: String = "",
#SerializedName("fax")
var fax: String = "",
#SerializedName("logitude")
var logitude: String = "",
#SerializedName("latitude")
var latitude: String = "",
#SerializedName("opendate")
var opendate: String = "",
#SerializedName("atmName")
var atmName: String = ""
)
#Step3: here is my API
#GET("api/location_api")
fun getBranchLocation(
#Query("requestCode") requestCode: String?
): Single<ArrayList<LocationdataModel>>
#Step4: My API Server from where I called my api
var okHttpClient: OkHttpClient? = OkHttpClient.Builder()
.connectTimeout(50, TimeUnit.SECONDS)
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(50, TimeUnit.SECONDS)
.callTimeout(50, TimeUnit.SECONDS)
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.header("Authorization", "myauth")
val request = requestBuilder.build()
chain.proceed(request)
}
.build()
private val api = Retrofit.Builder()
.baseUrl(baseurl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
.create(Api::class.java)
fun getBranchLocation(model: LocationModel): Single<ArrayList<LocationdataModel>> {
return api.getBranchLocation(
model.requestCode
)
}
#Step5: here is my ViewModel
fun getBranchLocation(model: LocationModel){
disposable.add(apiService.getBranchLocation(model)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<LocationdataModel>>() {
override fun onSuccess(model: List<LocationdataModel>) {
model.let {
getBranchLocationonList_response.value = model
}
}
override fun onError(e: Throwable) {
Log.e("error-->", e.message.toString())
e.printStackTrace()
}
})
Whate is the wrong with my code? Please help me

Unable to get API response with Retrofit and coroutines

I am trying to call a post API with Retrofit using coroutine but after hours of searching I am unable to find the mistake. There is no clear information about the error.
Below is the simple JSON I should be getting after posting the data.
{
"status": "OK",
"data": 5,
"message": null,
"code": "200"
}
api call from fragment
lifecycleScope.launchWhenStarted {
sharedPreferences.getString(AppConstant.token, "")?.let { authToken ->
viewModelSubProperty.sendDetail(authToken,propertyData)
}
}
where propertyData is the values I am send to the api.
ViewModel
suspend fun sendDetail(token: String,data: SubPropertyData) {
viewModelScope.launch(Dispatchers.IO) {
val responseResult = repository.sendDetail(token = token,data)
withContext(Dispatchers.Main) {
if(responseResult is Result.Success) {
Log.e("Viewmodel", "${responseResult.data}")
Log.e("Viewmodel", "$responseResult")
}
else if (responseResult is Result.Error && responseResult.exception.message == AppConstant.INTERNET_ERR_MSG)
EventBus.getDefault().post(WebServiceErrorEvent(null, true))
else if (responseResult is Result.Error)
EventBus.getDefault().post(WebServiceErrorEvent(responseResult))
}
}
}
DataSource
suspend fun sendDetail(token: String, data: SubPropertyData): Result<AddUpdateDataResponse> {
val serverResponse: Response<AddUpdateDataResponse>
return try {
serverResponse = serverApi.sendDetail(token,data)
if(serverResponse.isSuccessful)
Result.Success(serverResponse.body()!!)
else {
Log.e("what-code ", ""+serverResponse.errorBody())
Log.e("what-code ", serverResponse.errorBody()?.charStream().toString())
Result.Success(serverResponse.body()!!)
}
} catch (e: Throwable) {
if(e is NoConnectivityException)
Result.Error(IOException(AppConstant.INTERNET_ERR_MSG))
else
Result.Error(IOException(e.localizedMessage))
}
}
Api Call
#POST("api/addOrUpdateSubjectPropertyDetail")
suspend fun sendDetail(
#Header("Authorization") Authorization:String,
#Body data: SubPropertyData
) : Response<AddUpdateDataResponse>
Pojo class
data class AddUpdateDataResponse(
#SerializedName("code")
val code: String?,
#SerializedName("data")
val data: Int?,
#SerializedName("message")
val message: String? = null,
#SerializedName("status")
val status: String?
)
Logs
I/okhttp.OkHttpClient: --> POST https://abc/api/AddOrUpdateSubjectPropertyDetail
Content-Type: application/json; charset=UTF-8
Content-Length: 398
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiI0IiwiaHR0cDovL3NjaGVtYXnzK5gv0k4
I/okhttp.OkHttpClient: {"address":{"city":"Karachi","countryId":1,"countryName":"Pak","countyId":1,"countyName ":"SSS","stateId":11,"stateName":"Sindh","street":"akl","unit":"00","zipCode":"123"},"appraisedPropertyValue":100000.0,"floodInsurance":0.0,"homeOwnerInsurance":200.0,"isMixedUseProperty":true,"loanApplicationId":5,"mixedUsePropertyExplanation":"ashh","occupancyTypeId":1,"propertyTax":100.0,"propertyTypeId":1}
--> END POST (398-byte body)
--> END POST (398-byte body)
I/okhttp.OkHttpClient: <-- 500
https://abc/api/AddOrUpdateSubjectPropertyDetail (167ms)
I/okhttp.OkHttpClient: server: Microsoft-IIS/10.0
I/okhttp.OkHttpClient: x-powered-by: ASP.NET
date: Mon, 08 Nov 2021 22:31:31 GMT
I/okhttp.OkHttpClient: <-- END HTTP (0-byte body)
E/what-code: null
null
okhttp3.ResponseBody$BomAwareReader#3554188

Categories

Resources