1

I am using Retrofit for the first time. So don't mind the silliness please. I have 2 problems:

Problem 1. How to get a value for the key "userId" from the API JSON response. (When isSuccessful is true) Following is the response from the API.

API Response

I am able to get the userId from the following messy code, kindly show me a better way:

JSONObject jsonObject = new JSONObject(new Gson().toJson(response.body()));
                        JSONArray userInfoObject = jsonObject.getJSONArray("userInfo");
                        JSONObject userIDObject = userInfoObject.getJSONObject(0);
                        Long userId = userIDObject.getLong("userId");

Problem 2: onResponse when isSuccessful is false, the response.body is appearing null. So how do i access the API JSON response in this case. However on using the Okhttp logging i find the okhttp logs the response in this case as well but the Retrofit callback shows response.body() as null.

okhttp logs response but retrofit shows response body as null

Interface Class

public interface UserInfoClient {

@Headers("x-st-diagnostics-callerid: DKAPP-RETROFIT")
@POST("/api/User/")
Call<UserInfoModel> createUser(
        @Header("x-st-diagnostics-correlationid") String uniqueID,
        @Body UserInfoModel userInfo); 

}

DataModel

public class UserInfoModel {


private List userInfo;
private String statusMessage;
private Integer statusCode;
private Long userId;
private String userFullName, userPassword, userName, userEmail, userMobileNumber, userWebsite, userDateOfBirth, userProfileText, userProfileDisplayPhoto, userProfileCoverPhoto;

public UserInfoModel(Long userId, String userFullName, String userPassword, String userName, String userEmail, String userMobileNumber, String userWebsite, String userDateOfBirth, String userProfileText, String userProfileDisplayPhoto, String userProfileCoverPhoto) {
    this.userId = userId;
    this.userFullName = userFullName;
    this.userPassword = userPassword;
    this.userName = userName;
    this.userEmail = userEmail;
    this.userMobileNumber = userMobileNumber;
    this.userWebsite = userWebsite;
    this.userDateOfBirth = userDateOfBirth;
    this.userProfileText = userProfileText;
    this.userProfileDisplayPhoto = userProfileDisplayPhoto;
    this.userProfileCoverPhoto = userProfileCoverPhoto;
}

public Long getUserId() {
    return userId;
}

public List getUserInfo() {
    return userInfo;
}

public Integer getStatusCode() {
    return statusCode;
}

public String getStatusMessage() {
    return statusMessage;
}

}

Activity.java

public class ActivityUserInfo extends AppCompatActivity {

private static final String TAG = "ActivityUserInfo";
private EditText etUserId, etFullName, etPassword, etUsername, etEmail, etMobileNumber, etWebsite, etDob, etProfileTxt, etDispPhoto, etCoverPhoto;
private Button btnUpload;
public static Retrofit retrofit;



@Override
protected void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "onCreate: begins");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_user_info);

    etUserId = findViewById(R.id.et_userID);
    etFullName = findViewById(R.id.et_fullName);
    etPassword = findViewById(R.id.et_password);
    etUsername = findViewById(R.id.et_username);
    etEmail = findViewById(R.id.et_email);
    etMobileNumber = findViewById(R.id.et_mobileNumber);
    etWebsite = findViewById(R.id.et_website);
    etDob = findViewById(R.id.et_dob);
    etProfileTxt = findViewById(R.id.et_profileBio);
    etDispPhoto = findViewById(R.id.et_profilePhoto);
    etCoverPhoto = findViewById(R.id.et_coverPhoto);
    btnUpload = findViewById(R.id.btn_uploadUserInfo);

    btnUpload.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            UserInfoModel userInfo = new UserInfoModel(
                    Long.parseLong(etUserId.getText().toString()),
                    etFullName.getText().toString(),
                    etPassword.getText().toString(),
                    etUsername.getText().toString(),
                    etEmail.getText().toString(),
                    etMobileNumber.getText().toString(),
                    etWebsite.getText().toString(),
                    etDob.getText().toString(),
                    etProfileTxt.getText().toString(),
                    etDispPhoto.getText().toString(),
                    etCoverPhoto.getText().toString()
            );

            sendNetworkRequest(userInfo);
        }
    });

}

private void sendNetworkRequest(UserInfoModel userInfo){
    Log.d(TAG, "sendNetworkRequest: begins");
    //Create okhttp client
    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    //Adding logging interceptor tot he okHttp client
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    //set logging interceptor properties
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    //To redact a header from logging
    logging.redactHeader("Content-Type");
    //Disable logging interceptor in mode other than DEBUG.
    if (BuildConfig.DEBUG) {
        //Adding logging interceptor to the okHttpClient using okHttp client builder (conditional for debug only)
        okHttpClientBuilder.addInterceptor(logging);
    }

    //Create Retrofit Instance
    Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl("http://abc.ett.io")
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClientBuilder.build());

    Retrofit retrofit = builder.build();

    // Get client and call object for the request
    UserInfoClient client  = retrofit.create(UserInfoClient.class);

    String  uniqueID = UUID.randomUUID().toString(); //UUID for the header
    Call<UserInfoModel> call = client.createUser(uniqueID, userInfo);

    call.enqueue(new Callback<UserInfoModel>() {
        @Override
        public void onResponse(Call<UserInfoModel> call, Response<UserInfoModel> response) {
            Log.d(TAG, "1. onResponse: body: " + response.body() + "\n message: " + response.message()
                    + "\n code: " + response.code() + "\n headers: " + response.headers() + " errorBody: " + response.errorBody()
                    + "\n isSuccessful: " + response.isSuccessful() + "\n response: " + response);
                if (response.isSuccessful()) {
                    Toast.makeText(ActivityUserInfo.this, "Upload Successful.", Toast.LENGTH_SHORT).show();
                    try {
                        JSONObject jsonObject = new JSONObject(new Gson().toJson(response.body()));
                        JSONArray userInfoObject = jsonObject.getJSONArray("userInfo");
                        JSONObject userIDObject = userInfoObject.getJSONObject(0);
                        Long userId = userIDObject.getLong("userId");
                        Log.d(TAG, "onResponse: 1.5: " + userId);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    Log.d(TAG, "2. onResponse: 200: Upload Successful."
                           + " statusCode: " + response.body().getStatusCode()
                           + " statusMessage: " + response.body().getStatusMessage()
                           + " user ID: " + response.body().getUserId()
                           + "\n userInfo: " + response.body().getUserInfo().get(0));
                }
                else{
                    if(response.body() != null){
                        APIError apiError = ErrorUtils.parseError(response);
                        Toast.makeText(ActivityUserInfo.this, "3. Save not successful", Toast.LENGTH_SHORT).show();
                        Log.d(TAG, "3. onResponse: Save not successful"
                                +" ErrorUtils httpStatusCode: " + apiError.getHttpStatusCode()
                                +" ErrorUtils customCode: " + apiError.getCustomCode()
                                +" ErrorUtils message: " + apiError.getMessage());
                    }
                    else{
                        Log.d(TAG, "4. onResponse: response.body() is null");

                    }
                }
        }

        @Override
        public void onFailure(Call<UserInfoModel> call, Throwable t) {
            Log.d(TAG, "4. onFailure: t: " + t + " message: " + t.getMessage() + " toString: " + t.toString()
                    + " getCause: " + t.getCause() + " getStackTrace: " + t.getStackTrace());
            Toast.makeText(ActivityUserInfo.this, "Please check you internet connection.", Toast.LENGTH_SHORT).show();
        }
    });
}

}

Thanks for the help!

Saqib
  • 377
  • 4
  • 7

3 Answers3

1

You will need to break your UserInfoModel model class into two: one class to represent the "outer" object (with userInfo, statusCode, and statusMessage) and one class to represent the items in the array.

public class UserInfoResponse {

    private List<UserInfoItem> userInfo;
    private int statusCode;
    private String statusMessage;

    // ...
}
public class UserInfoItem {

     private String userFullName;
     private String userPassword;
     private String userName;

     // ...
}

You'll now make Call<UserInfoResponse> calls, and access the items in the list like this:

UserInfoResponse response = // ...
String name = response.getUserInfo()[0].getUserName();
Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • Thanks a lot @Ben P. Your solution is absolutely correct as is the one above. Just for clarification to others, i also created the getters in Root and Child Models and later in the onResponse the following code to get the userId: response.body().getUserInfo().get(0).getUserId() – Saqib Jul 17 '20 at 04:25
  • Remind that `userInfoList` should rename to `userInfo`. if not u have to use `@SerializedName`. – hassan moradnezhad Jul 17 '20 at 12:02
1

1st problem: How to get a value for the key "userId" from the API JSON response.

retrofit can automatically convert a JSON object into a POJO. so you just need to make your model classes.
for your JSON result, you need 2 model classes.

RootModel class

data class RootModel (
    var userInfo: mutableList<UserInfoChild> = ArrayList(),
    var statusCode: String = "",
    var statusMessage: String = ""
)  

UserInfoChild class

data class UserInfoChild (
    var userId: Int = 0,
    var userName: String? = "",
    var userPassword: String? = ""
    // add other fields
)  

next, change Call<UserInfoModel> to Call<RootModel>

in the end, in the onResponse method you can easily access to userId field.

val userId = response.body().userInfo[0].userId

2nd problem: status code is 500

use two backslashes in your URL may be the answer. if you share your base URL, then it'll be easier to solve it

hassan moradnezhad
  • 455
  • 2
  • 6
  • 26
0

My question had two problems: Problem 1 was solved by both hassan and Ben. As for the Problem 2 how do decode the response in case isSuccessful is false, i had to use the response.errorBody() instead of response.body() as the later was always null in my case for isSuccessful = false. The following code helped from this post: Getting json from retrofit's response errorBody

Gson gson = new Gson();
try {
    APIError errorResponse = gson.fromJson(response.errorBody().string(),APIError.class);

    Log.d(TAG, "onResponse: message: " + errorResponse.getMessage()
    + " httpStatusCode: " + errorResponse.getHttpStatusCode()
    + " customCode: " + errorResponse.getCustomCode()
    );
} catch (IOException e) {
    e.printStackTrace();
}

Where APIError.java is a class for return model in case of error, similar to root model suggested by both hassan and Ben:

public class APIError {
private int httpStatusCode;
private String customCode, message = "Unknown error.";

public APIError() {
}

public int getHttpStatusCode() {
    return httpStatusCode;
}

public String getCustomCode() {
    return customCode;
}

public String getMessage() {
    return message;
}

}

                        
Saqib
  • 377
  • 4
  • 7