How to parse response from remote server use json? I use retrofit and bearer token - json

By okhttp log I know I get response from remote server. But when I try to parse response I receive null! How to correct parse response from the server to get data?
My Data object:
#Parcelize
data class DataItem(
#field:SerializedName("name")
val name: String? = null,
) : Parcelable
MY Api:
interface Api {
#GET("v1/media/list/image")
suspend fun getImage(): DataItem
}
My Retrofit object:
private val httpClient = OkHttpClient
.Builder()
.protocols(listOf(Protocol.HTTP_1_1))
.addInterceptor(BearerTokenInterceptor(TOKEN))
.addInterceptor(logInterceptor())
//get api by retrofit
private val api: TVApi by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build()
retrofit.create(TVApi::class.java)
}
private fun logInterceptor() : HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
return interceptor
}
My try to parse the response:
private val scope = CoroutineScope(Dispatchers.IO)
private fun getNetworkResponse() {
scope.launch {
try {
Log.d(MY_TAG, "api: ${api.getVideo()}")
} catch (e: IOException) {
Log.e(MY_TAG, "IOException: $e")
} catch (e: HttpException) {
Log.e(MY_TAG, "HttpException: $e")
}
}
}
OKhttp log:
{"code":200,"message":"Success","data":[{"name"....}
My Log:
api: DataItem(extension=null, size=null, name=null, url=null)

Related

Access to Nested Json Kotlin

I don't know how to get data from nested Json
{
"results":[
{
"id":1,
"name":"Rick Sanchez",
"status":"Alive",
"species":"Human",
"type":"",
"gender":"Male",
Json looks like above, i want to get access to name variable.
My code:
Data class:
data class Movie(
#Json(name = "results") val results: List<MovieDetail>
)
data class MovieDetail(
#Json(name = "name") val name: String
)
ApiService:
private const val BASE_URL = "https://rickandmortyapi.com/api/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface MovieApiService {
#GET("character")
suspend fun getMovies(): List<Movie>
}
object MovieApi {
val retrofitService : MovieApiService by lazy {
retrofit.create(MovieApiService::class.java)
}
}
And ViewModel:
private val _status = MutableLiveData<String>()
val status: LiveData<String> = _status
init {
getMovies()
}
private fun getMovies() {
viewModelScope.launch {
val listResult = MovieApi.retrofitService.getMovies()
_status.value = "Success: ${listResult.size} names retrieved"
}
}
For plain Json there is no problem but i don't know how to get access to this nested variables, i think that i have to use "results" variable from data class but i don't know where and how.
During running app i've got error: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $
You should change
#GET("character")
suspend fun getMovies(): List<Movie>
To:
#GET("character")
suspend fun getMovies(): Movie
You are receiving object and not list of objects

Kotlin Retrofit2 make a request with body as raw JSON

So, I have a challenge to make a sign in and sign up features in Android App but I still confuse about how to implement a kotlin retrofit function that need a parameter body raw json. Here is the API looks like in postman
I am using MVVM design pattern and Hilt for Dependency Injection,
these are some of my code related to this case
#Provides
#Singleton
fun provideRetrofit(): Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
#Provides
#Singleton
fun provideApi(retrofit: Retrofit): Api = retrofit.create(Api::class.java)
this is my function
#POST("signin")
suspend fun signIn(
#Body raw: JSONObject
): SignInResponse
this is how I call the function in view model
fun signIn(email: String, password: String) {
val param = JSONObject().apply {
add("email", email)
add("password", password)
}
viewModelScope.launch {
try {
signInResponse.value = api.signIn(param)
} catch (e: Exception) {
Log.d(TAG, "signInError: $e")
}
}
}
but it didn't work. I also have tried to change the parameter type from JSONObject to string but it still didn't work
I have just found a solution.
The solution is very simple, I just need to change the parameter type from JSONObject to JsonObject. Here is my final code
#POST("signin")
suspend fun signIn(
#Body raw: JsonObject
): SignInResponse
fun signIn(email: String, password: String) {
val param = JsonObject().apply {
addProperty("email", email)
addProperty("password", password)
}
viewModelScope.launch {
try {
signInResponse.value = api.signIn(param)
} catch (e: Exception) {
Log.d(TAG, "signInError: $e")
}
}
}
I have tried it and it works fine. I hope it can help you if you guys are facing the same case as me.

How do I get the json object in the request body in webflux?

I am trying to parse json data from request body to JsonOject.
Spring Reactive get body JSONObject using ServerRequest
// it didn't work.
JSONObject bodyData = serverRequest.bodyToMono(JSONObject.class).toProcessor().peek();
I tried it as referenced in the link above, but it didn't work.
I want to know why this is.
For the test, two router beans were created as shown below.
// router
#Bean
public RouterFunction<ServerResponse> routeJsonBodyPOST2(JsonObjectHandler jsonObjectHandler) {
return route(RequestPredicates.POST("/json/post2")
.and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), jsonObjectHandler::getStringByJsonObject);
}
#Bean
public RouterFunction<ServerResponse> routeJsonBodyPOST3(JsonObjectHandler jsonObjectHandler) {
return route(RequestPredicates.POST("/json/post3")
.and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), jsonObjectHandler::getJsonObject);
}
// handler
// I checked the json data in onNext. I understood this.
public Mono<ServerResponse> getStringByJsonObject(ServerRequest request) {
Mono<String> response = request.bodyToMono(JSONObject.class).log()
.map(jsonObject -> {
String name = (String) jsonObject.get("name");
System.out.println(name);
return name;
});
return ServerResponse.ok()
.body(response, String.class);
}
// Only onComplete is called and exits. I don't understand this.
public Mono<ServerResponse> getJsonObject(ServerRequest request) {
Mono<JSONObject> response = request.bodyToMono(JSONObject.class).log();
response.onErrorResume(error -> {
System.out.println(error);
return Mono.error(error);
}).subscribe(
jsonObject -> System.out.println("test : " + jsonObject),
error -> System.out.println(error.getMessage()),
() -> System.out.println("complete")
);
// Mono<JSONObject> jsonObjectMono = request.bodyToMono(JSONObject.class);
// jsonObjectMono.subscribe(System.out::println);
// JSONObject peek = jsonObjectMono.toProcessor().peek();
// System.out.println(peek);
// just for test.
return ServerResponse.ok().build();
}

kotlin get a simple message using retrofit

Hello i am having big problems here this is my first time using retrofit and i am new to kotlin, i don't know why this piece of code is not working.
This is my retrofit client
private const val BASE_URL = "https://89a6t4gtke.execute-api.eu-west-3.amazonaws.com/Prod/"
val instance : IApi by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(IApi::class.java)
}
This is my response class
data class DefaultResponse(val message: String) {}
This is my response:
{
"message": "GET"
}
Interface
interface IApi {
#GET("hello")
fun returnHello():Call<DefaultResponse>
}
The call
toast_button.setOnClickListener{
RetrofitClient.instance
.returnHello()
.enqueue(object: Callback<DefaultResponse>{
override fun onFailure(call: retrofit2.Call<DefaultResponse>, t: Throwable) {
Toast.makeText(context,t.message + "bla",Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: retrofit2.Call<DefaultResponse>,response: Response<DefaultResponse>) {
Toast.makeText(context, "empty?",Toast.LENGTH_SHORT)
}
})
}
No toast messages show, i had an error show once when i made my api just return a string and not a json string but now there is no error as i fixed it.
You should change to your Retrofit Client as following code;
class RetrofitClient {
companion object {
fun getClient(): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://89a6t4gtke.execute-api.eu-west-3.amazonaws.com/Prod/")
.build()
}
}
}
Then add this in your activity ;
val service = RetrofitClient.getClient().create(IApi::class.java)
val call = service.returnHello()
val resp: DefaultResponse? = call.clone().execute().body()
if (resp != null) {
println("your response is -> $resp")
}

Encoding collection to json array in jsr 356

I am learning websockets and my webapp is using jsr 356 library. I followed the tutorials and I can encode/decode POJOs, however I can't find examples on how to serialize either arrays or collections to JSON.
This is what I am doing to encode my data:
#Override
public String encode(ScanPlus scan) throws EncodeException {
JsonObject jsonObject = createJsonObject(scan);
return jsonObject.toString();
}
private JsonObject createJsonObject(ScanPlus scan) {
JsonObject jsonObject = Json.createObjectBuilder()
.add("scan", scan.getCode())
.add("creationdate", String.valueOf(scan.getCreationDate()))
.add("username", scan.getUserName())
.build();
return jsonObject;
}
public String encode(ArrayList<ScanPlus> scans) throws EncodeException {
JsonArrayBuilder jsonArray = Json.createArrayBuilder();
for (ScanPlus scan : scans) {
JsonObject jsonObject = createJsonObject(scan);
jsonArray.add(jsonObject);
}
return jsonArray.toString();
}
This is how I send the data to the encoder:
#OnOpen
public void onOpen(Session session, #PathParam("username") String username) {
...
session.getBasicRemote().sendObject(scans);
}
And this is the exception I am getting:
javax.websocket.EncodeException: No encoder specified for object of class [class java.util.ArrayList]
Could anyone give me a hint on how to do it?
thanks
You need to create Encoder<ArrayList<ScanPlus>>; Encoder<ScanPlus> is not enough..