Retrofit response.body() is null, but API JSON visible in Logcat - java

I have my Android Java REST API code working perfectly with another REST API that uses a JSON object hierarchy (aka List-bracket-object1-bracket object2). Now I moved on to a simplified API (googlemaps timezone - just one simple object with 5 fields) but I cannot figure out how to get the google data into my object.
I am getting status code 200, I can see the googlemaps output/JSON in my logcat but my object is null. I read this post "0 Retrofit 2: response.body() is null, but status code is 200 2" and I am sure I have the same problem but I do not know how to adjust my object model to fix it. Here is my stuff...
1 - API/URL I am hitting:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=xxxxxx
2 - What the API/URL returns (from my LogCat)
D/OkHttp: {
D/OkHttp: "dstOffset" : 0,
D/OkHttp: "rawOffset" : -28800,
D/OkHttp: "status" : "OK",
D/OkHttp: "timeZoneId" : "America/Los_Angeles",
D/OkHttp: "timeZoneName" : "Pacific Standard Time"
D/OkHttp: }
3 - Logcat showing object is null
Log.e(" main ", " apt " + response.body().getResults());
E/ main: apt null
4 - The calling routine
ApiInterface apiService = ApiClient.getClient(1).create(ApiInterface.class);
Call<ApiTimes> call = apiService.getTime(location,epoch,GOOG_TZ_KEY);
call.enqueue(new Callback<ApiTimes>() {
#Override
public void onResponse(Call<ApiTimes> call, Response<ApiTimes> response) {
int statusCode = response.code();
Log.e(" main ", " apt " + response.body().getResults());
}
#Override
public void onFailure(Call<ApiTimes> call, Throwable t) {
Log.e(" getFS ", t.toString());
}
});
5 - The object.
public class ApiTime {
#SerializedName("apitime")
private ApiTime apitime;
#SerializedName("dstOffset")
#Expose
private Long dstOffset;
#SerializedName("rawOffset")
#Expose
private Long rawOffset;
#SerializedName("status")
#Expose
private String status;
#SerializedName("timeZoneId")
#Expose
private String timeZoneId;
#SerializedName("timeZoneName")
#Expose
private String timeZoneName;
public ApiTime(Long dstOffset, Long rawOffset, String status, String timeZoneId, String timeZoneName) {
this.dstOffset = dstOffset ;
this.rawOffset = rawOffset ;
this.status = status ;
this.timeZoneId = timeZoneId ;
this.timeZoneName = timeZoneName ;
}
public ApiTime getResults() {
return apitime;
}
public void setResults(ApiTime apitimes) {
this.apitime = apitime;
}
public Long getDstOffset() {
return dstOffset;
}
public void setDstOffset(Long dstOffset) {
this.dstOffset = dstOffset;
}
public Long getRawOffset() {
return rawOffset;
}
public void setRawOffset(Long rawOffset) {
this.rawOffset = rawOffset;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getTimeZoneId() {
return timeZoneId;
}
public void setTimeZoneId(String timeZoneId) {
this.timeZoneId = timeZoneId;
}
public String getTimeZoneName() {
return timeZoneName;
}
public void setTimeZoneName(String timeZoneName) {
this.timeZoneName = timeZoneName;
}
#Override
public String toString() {
return "ApiTime{" +
"dstOffset=" + dstOffset +
", rawOffset=" + rawOffset +
", status='" + status + '\'' +
", timeZoneId='" + timeZoneId + '\'' +
", timeZoneName='" + timeZoneName + '\'' +
'}';
}
}
6 - Here are the two invocation routines:
public class ApiClient {
public static final String URL0 = blah blah
public static final String URL1 = "https://maps.googleapis.com/maps/api/timezone/";
private static Retrofit retrofit = null;
public static String BASE_URL = "www.google.com";
public static Retrofit getClient(int url) {
BASE_URL = URL0;
if (url == 1){
BASE_URL = URL1;
}
//The following block of code generates OkHttpClient loggging to LogCat.
//Only use if debugging.
OkHttpClient.Builder client = new OkHttpClient.Builder();
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.addInterceptor(loggingInterceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
//Retrofit retrofit = new Retrofit.Builder()
// .baseUrl(BASE_URL)
// .addConverterFactory(GsonConverterFactory.create())
// .build();
return retrofit;
}
}
public interface ApiInterface {
#GET("json")
Call<ApiTimes> getTime(#Query("location") String location, #Query("timestamp") String timestamp, #Query("key") String key);
}

Looks like you should use ApiTime instead of ApiTimes
I don't see #SerializedName("apitimes") in your json in logs
public interface ApiInterface {
#GET("json")
Call<ApiTime> getTime(#Query("location") String location, #Query("timestamp") String timestamp, #Query("key") String key);
}
-
ApiInterface apiService = ApiClient.getClient(1).create(ApiInterface.class);
Call<ApiTime> call = apiService.getTime(location, epoch, GOOG_TZ_KEY);
call.enqueue(new Callback<ApiTime>() {
#Override
public void onResponse(Call<ApiTime> call, Response<ApiTime> response) {
int statusCode = response.code();
Log.e("main", "apt " + response.body());
}
#Override
public void onFailure(Call<ApiTime> call, Throwable t) {
Log.e("getFS ", t.toString());
}
});
-
public class ApiTime {
#SerializedName("dstOffset")
private Long dstOffset;
#SerializedName("rawOffset")
private Long rawOffset;
#SerializedName("status")
private String status;
#SerializedName("timeZoneId")
private String timeZoneId;
#SerializedName("timeZoneName")
private String timeZoneName;
// getters here
}

Related

Expected a string but was BEGIN_OBJECT at line 3 column 4 path $.SUCCESS

I made a post request method with Retrofit2 but I encountered this problem on my response.
Expected a string but was BEGIN_OBJECT at line 3 column 4 path $.SUCCESS
The response should be
{
"SUCCESS" :
{
"200" : "access granted",
"ra" : "approved",
"la" : "approved",
"ch" : "approved"
}
}
I uses this code for the post request
#POST("login")
Call<Post> createPost(#Body Post post);
And for the POJO class
public class Post {
private String anthony;
private String SUCCESS;
public Post(String name) {
this.anthony = name;
}
public String getSUCCESS() {
return SUCCESS;
}
}
For the method I use the following code
private void createPost() {
Post post = new Post("mypassword");
Call<Post> call = jsonPlaceHolderApi.createPost(post);
call.enqueue(new Callback<Post>() {
#Override
public void onResponse(Call<Post> call, Response<Post> response) {
if (!response.isSuccessful()) {
textViewResult.setText("Code: " + response.code());
return;
}
Post postResponse = response.body();
String content = "";
content += "Code: " + response.code() + "\n";
content += "S" + postResponse.getSUCCESS();
textViewResult.setText(content);
}
#Override
public void onFailure(Call<Post> call, Throwable t) {
textViewResult.setText(t.getMessage());
}
});
}
Does anyone know what's wrong with my code? I expected to get the response inside the "SUCCESS" json object.
You expect SUCCESS to be an object in your wanted response but you have defined it as a String in your Post class. You should use an object for SUCCESS instead.
public class Post {
private String anthony;
private PostSuccess SUCCESS;
public Post(String name) {
this.anthony = name;
}
public PostSuccess getSUCCESS() {
return SUCCESS;
}
}
public class PostSuccess {
#JsonProperty("200")
private String _200;
private String ra;
private String la;
private String ch;
}

java.lang.IllegalStateExeption: Expectet a string but was BEGIN_ARRAY at line 1 column 16 path $[0].questions

I looked at some other threads about this topic and integrated the solution it offered but it still throws the same error. this is the first time i try to call an api on android. here i want to 'GET' an array of objects. There is no stack trace since the app does not crash. i think the problem has to do with the array of questions.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.100:3000/api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceHolderApi jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Call<List<Post>> call = jsonPlaceHolderApi.getPosts();
call.enqueue(new Callback<List<Post>>() {
#Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
if (!response.isSuccessful()){
textViewResult.setText("Code: " + response.code());
return;
}
List<Post> posts = response.body();
for (Post post : posts){
String content = "";
content += "Doctor: " + post.getDoctor() + "\n";
content += "Name: " + post.getName() + "\n";
content += "Questions: " + post.getQuestions() + "\n\n";
textViewResult.append(content);
}
}
#Override
public void onFailure(Call<List<Post>> call, Throwable t) {
textViewResult.setText(t.getMessage());
}
});
here is an example of the json data:
[
{
"questions":[...],
"_id":"5f42954a7e252b48ec3564b6",
"name":"Lifestyle",
"doctor":"doctoremail#gmail.com",
"__v":0
},
{
"questions":[...],
"_id":"5f4299687e252b48ec3564b7",
"name":"Headache",
"doctor":"doctoremail#gmail.com",
"__v":0
},
{
"questions":[...],
"_id":"5f429b2f7e252b48ec3564b9",
"name":"Foot pain",
"doctor":"doctoremail#gmail.com",
"__v":0
}
]
I fixed it. the problem was that the 'questions' property in my model was of type string in stead of List String
package com.example.medlog;
import java.util.List;
public class Post {
private String name;
private String doctor;
private List<String> questions;
public String getName() {
return name;
}
public String getDoctor() {
return doctor;
}
public List<String> getQuestions() {
return questions;
}
}

Why I am getting JsonSyntaxException Error

I am new to retrofit (I was using volley before), before this I was doing fine with retrofit until this error comes :-
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected int but was Boolean at line 8 column 37 path
$.response.data.book_service_id
I tried with every solution provided in this site but could not help myself since I am new to retrofit.
I think it's because of the JSON parsing error. I don't know how to handle it.
This may be a duplicate question but please help.
Below is my code:
Request and fetching data:
private void makeBookingRequest(String position) {
final CustomProgressDialog dialog = new CustomProgressDialog();
dialog.show(getSupportFragmentManager(),"tag");
SharedPreferences preferences = getSharedPreferences("MYSharedPref",MODE_PRIVATE);
String sessionkey = preferences.getString("sessionkey",null);
System.out.println(sessionkey);
String serviceId = position;
System.out.println(position);
APIEndPoints endPoints = Url.getInstance().create(APIEndPoints.class);
Call<Book> call = endPoints.makeBookingRequest(serviceId,sessionkey);
call.enqueue(new Callback<Book>() {
#Override
public void onResponse(Call<Book> call, retrofit2.Response<Book> response) {
dialog.dismiss();
if (!response.isSuccessful()) {
Toast.makeText(HomeActivity.this, "server is not responding", Toast.LENGTH_SHORT).show();
}
else if(response.body() != null){
Book bookData = response.body();
String message = bookData.response.message;
Toast.makeText(HomeActivity.this, message, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<Book> call, Throwable t) {
dialog.dismiss();
Toast.makeText(HomeActivity.this, "Error" + t.getLocalizedMessage(),
Toast.LENGTH_SHORT).show();
System.out.println(t);
}
});
}
Model Class:
package com.medpal.medpal_client.Models;
import com.google.gson.annotations.SerializedName;
public class Book {
#SerializedName("response")
public ResponseEntity response;
public class ResponseEntity{
#SerializedName("data")
public DataEntity data;
#SerializedName("secondary_message")
public String secondary_message;
#SerializedName("message")
public String message;
#SerializedName("code")
public int code;
}
public class DataEntity {
#SerializedName("book_service_id")
public int book_service_id;
}
}
APIENDPOINTS
#FormUrlEncoded
#Headers({"apikey: testapikey", "Content-Type:application/x-www-form-urlencoded" })
#POST("service/accept?")
Call<Book> makeBookingRequest(
#Field("service_id") String ServiceId,
#Field("session_key") String sessionKey);
URL class
public class Url {
public static final String base_url = "http://www.medpal.net/api/v1/";
public static final String serviceUrl = "http://www.medpal.net/api/v1/services?";
public static Retrofit retrofit;
public static Retrofit getInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(base_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Finallyy Response from server:
{
"response": {
"error": [],
"code": 200,
"message": "Service booked",
"secondary_message": "Service booked successfully",
"data": {
"book_service_id": 35
}
}
}
To detect the problem exactly, You need to use an interceptor to log the server response, to log the server responses you can use OkHttp3 here is an example of it.
private OkHttpClient provideOkhttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(15000, TimeUnit.MILLISECONDS);
client.writeTimeout(70000, TimeUnit.MILLISECONDS);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.addInterceptor(interceptor);
return client.build();
}
And add this to your Retrofit.Builder
.client(provideOkhttpClient())
And these are for Gradle
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
If you do this you will see everything you sent and got from the server at your Logcat.

Failed to show the output from json object

I want to parse json from json object and put it on textview. I tried some method but failed. The error:
expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
API SERVICE: Full ver http://139.255.86.189:83/service/api/checklistpertanyaan/1
{
"success": true,
"data": [
{
"idRchecklistpompa": "1",
"nmChecklist": "Membersihkan Body Pompa"
},
{
"idRchecklistpompa": "2",
"nmChecklist": "Membersihkan Kabel Tray Pompa"
},
Harian.java
public class Harian {
#SerializedName("idRchecklistpompa")
#Expose
private String idRchecklistpompa;
#SerializedName("nmChecklist")
#Expose
private String nmChecklist;
public String getIdRchecklistpompa() {
return idRchecklistpompa;
}
public String getNmChecklist() {
return nmChecklist;
}
public void setIdRchecklistpompa(String idRchecklistpompa) {
this.idRchecklistpompa = idRchecklistpompa;
}
public void setNmChecklist(String nmChecklist) {
this.nmChecklist = nmChecklist;
}
}
MainActivity.java
public class HarianActivity extends AppCompatActivity {
private TextView textViewResult;
/*private static String url = "http://139.255.86.189:83/service/api/checklistpertanyaan/1";*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_harian);
textViewResult = findViewById(R.id.text_view_result);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://139.255.86.189:83/service/api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
HarianApi harianApi = retrofit.create(HarianApi.class);
Call<List<Harian>> call = harianApi.getHarian();
call.enqueue(new Callback<List<Harian>>() {
#Override
public void onResponse(Call<List<Harian>> call, Response<List<Harian>> response) {
if (!response.isSuccessful()) {
textViewResult.setText("CodeL " + response.code());
return;
}
List<Harian> harians = response.body();
for (Harian harian : harians) {
String content = "";
content += "ID " + harian.getIdRchecklistpompa() + "\n";
content += "NAMA " + harian.getNmChecklist() + "\n";
textViewResult.append(content);
}
}
#Override
public void onFailure(Call<List<Harian>> call, Throwable t) {
textViewResult.setText(t.getMessage());
}
});
}
}
I would expect JSON that encapsulated a List of Harians to look like this:
[
{
"idRchecklistpompa": "1",
"nmChecklist": "Membersihkan Body Pompa"
},
{
"idRchecklistpompa": "2",
"nmChecklist": "Membersihkan Kabel Tray Pompa"
}
]
Instead, yours begins with:
{
"success": true,
"data": [
...
So it isn't correct for your API to return List<Harian>. Instead, your API should return a different class which looks more like:
public class Container {
#SerializedName("success")
private boolean success;
#SerializedName("data")
List<Harian> data;
public static class Harian {
#SerializedName("idRchecklistpompa")
#Expose
private String idRchecklistpompa;
#SerializedName("nmChecklist")
#Expose
private String nmChecklist;
public String getIdRchecklistpompa() {
return idRchecklistpompa;
}
public String getNmChecklist() {
return nmChecklist;
}
public void setIdRchecklistpompa(String idRchecklistpompa) {
this.idRchecklistpompa = idRchecklistpompa;
}
public void setNmChecklist(String nmChecklist) {
this.nmChecklist = nmChecklist;
}
}
}
And have your Retrofit API return Container rather than List<Harian>
Not sure if I understand but, to debug the problem what I would do is:
1.- Check as a String that response is a well formed JSON String.
Log.d(TAG, "My JSON String: " + response.code());
1.5.- Check if that string is a JSONObject or a JSONArray
2.- Probably try to create a JSONObject/JSONArray from that String to see if it triggers an exception.
try {
JSONObject jsonObject = new JSONObject(response.code());
} catch (JSONException e) {
e.printStackTrace();
}
3.- Try to parse the JSONObject but checking for exceptions:
try {
String nmChecklist = jsonObject.getString("nmChecklist");
} catch (JSONException e) {
e.printStackTrace();
}
4.- If you want to avoid exceptions since some objects may or may not have a key or value:
String nmChecklist = jsonObject.has("nmChecklist") && !jsonObject.isNull("nmChecklist") ? jsonObject.getString("nmChecklist") : null;
I hope this helps.
I think there is some problem with your class. The response is different from your pojo class. See json to pojo and create your Model as per the generated pojo.

How to fix Expected BEGIN_OBJECT but was STRING in Retrofit? [duplicate]

This question already has an answer here:
How to fix Expected BEGIN_OBJECT in Retrofit?
(1 answer)
Closed 4 years ago.
In my application i want use Retrofit for get some data from server.
I write below codes but when run application and call api show me below error :
E/socketLogResponse: Err : com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
Please see my above codes and help me
API response from server :
{
"status": "ok",
"time": 0.014972925186157227
}
ApiService interface :
#POST("api/log")
Call<SocketPingResponse> getSocketPingLog(#Header("jwt") String jwt, #Body SocketPingBodySendData socketPingBodySendData);
SocketPingResponse class :
public class SocketPingResponse {
#SerializedName("status")
#Expose
private String status;
#SerializedName("time")
#Expose
private Double time;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Double getTime() {
return time;
}
public void setTime(Double time) {
this.time = time;
}
}
SocketPingBodySendData class :
public class SocketPingBodySendData {
#SerializedName("auction_id")
#Expose
int auction_id;
#SerializedName("data")
#Expose
List<SocketPingEntity> data;
public int getAuction_id() {
return auction_id;
}
public void setAuction_id(int auction_id) {
this.auction_id = auction_id;
}
public List<SocketPingEntity> getData() {
return data;
}
public void setData(List<SocketPingEntity> data) {
this.data = data;
}
}
Api call codes in activity :
pingEntityList.addAll(socketPingDatabase.socketPingDao().getSocketPingEntityList());
SocketPingBodySendData pingBodySendData = new SocketPingBodySendData();
pingBodySendData.setAuction_id(auctionID);
pingBodySendData.setData(pingEntityList);
Toast.makeText(context, ""+pingEntityList.size(), Toast.LENGTH_SHORT).show();
Call<SocketPingResponse> pingResponseCall = apis.getSocketPingLog(jwtToken, pingBodySendData);
pingResponseCall.enqueue(new Callback<SocketPingResponse>() {
#Override
public void onResponse(Call<SocketPingResponse> call, Response<SocketPingResponse> response) {
if (response.body() != null) {
Toast.makeText(context, response.body().getStatus(), Toast.LENGTH_SHORT).show();
if (response.body().getStatus().equals("ok")) {
pingEntityList.clear();
socketPingDatabase.socketPingDao().deleteAll();
}
}
}
#Override
public void onFailure(Call<SocketPingResponse> call, Throwable t) {
Log.e("socketLogResponse", "Err : " + t.toString());
}
});
ApiClient class :
public class ApiClient {
private static final String BASE_URL = Constants.SERVER;
private static Retrofit retrofit = null;
private static Context context;
public static Retrofit getClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.interceptors().add(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("X-Client-Version", Constants.getAppVersionName()).build();
return chain.proceed(request);
}
});
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("uuid", Constants.getUUID(Constants.currentActivity)).build();
return chain.proceed(request);
}
});
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("agent", Constants.getAgent()).build();
return chain.proceed(request);
}
});
OkHttpClient client2 = client
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client2)
.build();
}
return retrofit;
}
}
How can i fix this issue?
I think the problem returns string when no data is returned. Backend can usually do such errors. this error had happened to me before. you should check the response json when no data is available
Retrofit is typesafe library. It means that it waits only particular (predefined) types of objects. If server sends something else - it crashes with error. This is your case. Just check raw server response and you'll see what's wrong.
Try changing your API call
from
#POST("api/log")
Call<SocketPingResponse> getSocketPingLog(#Header("jwt") String jwt, #Body SocketPingBodySendData socketPingBodySendData);
to
#POST("api/log")
Call<String> getSocketPingLog(#Header("jwt") String jwt, #Body SocketPingBodySendData socketPingBodySendData);
pingEntityList.addAll(socketPingDatabase.socketPingDao().getSocketPingEntityList());
SocketPingBodySendData pingBodySendData = new SocketPingBodySendData();
pingBodySendData.setAuction_id(auctionID);
pingBodySendData.setData(pingEntityList);
Toast.makeText(context, ""+pingEntityList.size(), Toast.LENGTH_SHORT).show();
Call<String> pingResponseCall = apis.getSocketPingLog(jwtToken, pingBodySendData);
pingResponseCall.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.body() != null) {
//Convert here your string response to Other POJO format
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("socketLogResponse", "Err : " + t.toString());
}
});

Categories

Resources