Retrofit 2 Post response to different class - android

I want to read response as a custom class but I have to use ResponseBody as a parameter in Post method.
Post interface :
public interface IPostPhoneToken {
#FormUrlEncoded
#POST()
Call<ResponseBody> postPhoneToken(
#Field("data[UserPhoneToken][first_name]") String firstName,
...
#Url String endpoint);
}
Problem is here :
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.isSuccessful()){
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("Failed");
}
});
I want to change ResponseBody with different Class to be able to read response values.
Thanks.

You can use Response<JsonElement> and get as Json object your response and after that use any json deserializer to convert to your class.
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if(response.isSuccessful()){
JsonElement jsonElement = response.body();
JsonObject objectWhichYouNeed = jsonElement.getAsJsonObject();
//use any json deserializer to convert to your class.
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
System.out.println("Failed");
}
});

Related

Displaying data from a Json array using Retrofit in a textview

I'm trying to display data from a Json array using Retrofit on a textfield, however the textfield doesn't show anything. Kindly help.
Here is the request from the retrofit service
#GET("users/:email")
Call<User> getUser(#Query("email") String email );
And how i try to make it display in the text field
Call<User> users = api.getUser(email);
users.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
txtName.setText(response.body().getUserEmail());
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
});
The response from postman looks like this
[
{
"name": "First Name",
"email": "myemail#gmail.com",
"phone": "00000000"
}
]
First of all you are getting JSON array in response so you need to change your callback accordingly.
change your service call to this
Call<List<User>> getUser(#Query("email") String email );
then
Call<List<User>> users = api.getUser(email);
users.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
List<User> users = response.body();
if(users!=null && users.size()>0)
{
txtName.setText(response.body().get(0).getUserEmail());
}
else
{
txtName.setText("No User found");
}
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Log.d("USER",t.getMessage());
}
});

retrofit2 get json object without jsonArray name

I want to take jsonobject using retrofit without class definition, the result is null. How about this ?
I have a get query with JSON response :
[{"Kuota":"12"}]
This my code get data JSONObject .
public void GetKuota(String Key) {
IBookingService iBookingService = APIClient.getClient().create(IBookingService.class);
Call<ResponseBody> call = iBookingService.getKuota(Key);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(!response.isSuccessful()) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(response.body().toString());
// Log.d(jsonObject.getString("Kuota"));
datas =String.valueOf(jsonObject.getInt("Kuota"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
Suggestion:
Create a model class, lets say Kuota
public class Kuota {
public int Kuota;
}
Then rewrite the Retrofit callback like this.
iBookingService.getKuota(Key).enqueue(new Callback<List<Kuota>>() {
#Override
public void onResponse(Call<List<Kuota>> call, Response<List<Kuota>> response) {
if(response.code() == 200 && response.body() != null) {
try {
for(Kuota k: response.body()) {
Log.d("Kuota Value" , " " + k.kuota);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<List<Kuota>> call, Throwable t) {
}
});
Replace iBookingService data holder from ResponseBody to List< Kuota >
if not added, please add the JSON serializer while setting up Retrofit instance.
new Retrofit.Builder()
.......
.addConverterFactory(GsonConverterFactory.create())
.build();
And also add the dependency
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

How to get response from server as String ,Retrofit 1.9

I would like to handle my response from server , but I don't know how JSON (from server) its looks like. So I tried to display response as String , but I cant do it. Is it possible to display response as String? And then handle the response correctly. thanks
(Retrofit 1.9)
LoginService loginService = RetrofitClient.createService(LoginService.class);
loginService.searchOffer(getToken(), offerSearch, new Callback<String>() {
#Override
public void success(String offerSearchRequestResource, Response response) {
String responseFromSercer = response.getBody();
}
#Override
public void failure(RetrofitError error) {
}
});
change your response model to
JSONObject (from Gson)
then in your
public void success(...){response.body.toString();}
like this:
Call<JsonObject> call = YOUR_API_INTERFACE().YOUR_METHOD(YOUR_PARAMS);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, #NonNull Response<JsonObject> response) {
if(response.isSuccessful()) {
String responseFromSercer = response.body.toString();
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
.....
}
});
If you are sure the request runs successfully and there is a response back...use
System.out.println(response.getBody());
i'd also suggest you add Logging Interceptors here so that you can get a detailed view of all your API calls

How to handle response which can be different Type in Retrofit 2

In WebApi returned JSON's field can be of different class:
{ someField:"some string" }
{ someField: { "en" : "some string", "ka" : "რამე სტრინგი" } }
I've seen some solutions, but it was on previous versions of Retrofit.
How would my pojo class look like and what can i use to parse this dynamic json?
For your case you can use Call<JsonElement> as response type and parse it in response:
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if(response.isSuccessful()){
JsonElement jsonElement = response.body();
if(jsonElement.isJsonObject()){
JsonObject objectWhichYouNeed = jsonElement.getAsJsonObject();
}
// or you can use jsonElement.getAsJsonArray() method
//use any json deserializer to convert to your class.
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
System.out.println("Failed");
}
});

Retrofit 2.0 handle error in json response

I'm working on Retrofit 2.0 (which is awesome) to handle API responses.
All works well when API answers with success, I return the converted object wished from the json response
Here an example of request:
ServiceAPI.getUser(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body(); // user is my POJO
}
#Override
public void onFailure(Call<User> call, final Throwable t) {
Log.e(TAG, "Error: " + t.getMessage())
}
});
When response is done, I return the POJO (which is my main purpose), not the json to parse in order to avoid boilerplate.
So here my code to handle this :
// the interface to handle calls
protected interface ServiceAPI {
#GET("/user/{userId}")
Call<User> getUser(#Path("userId") String userId);
}
// the GSON part for converting data
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeAdapterFactory())
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
// an interceptor to log requests responses
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(new LogJsonInterceptor())
.build();
// the retrofit builder
retrofit = new Retrofit.Builder()
.baseUrl(mBaseUrl)
.client(okClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
// the interceptor to log requests
public static class LogJsonInterceptor implements Interceptor {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
String rawJson = response.body().string();
Log.d(TAG, rawJson);
// Re-create the response before returning it because body can be read only once
return response.newBuilder().body(ResponseBody.create(response.body().contentType(), rawJson)).build();
}
}
// here the magic to handle json response and get data from "data" json key
public static class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
private RestError error;
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("data")) {
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
// here the method to call from an activity for example to get an User
public static void getUser(final String userId, final Callback<User> callback) {
serviceAPI.getUser(userId).enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
callback.onResponse(call, response);
}
#Override
public void onFailure(Call<User> call, Throwable t) {
callback.onFailure(call, t);
}
});
}
// here the call from the Activity
ServiceAPI.getUser(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
}
#Override
public void onFailure(Call<User> call, final Throwable t) {
Log.e(TAG, "Error: " + t.getMessage())
}
});
// when API answers with success (code 200 from headers)
{
"data":
{
"id":1,
"name":"myname",
"email":"myemail"
}
}
So all works well here because I get response from "data" and convert the response into my POJO
PROBLEM:
But when API answers with an error (also with code 200 from headers), I get this:
{
"error":
{
"code":200
"type":"OAuth_exception",
"message":"You need an access token to get an user",
}
}
The problem is that the retrofit "response" is nevertheless successfull and the errorBody is null
So here, I would to convert this into a RestError POJO (below) and send it inside the call when calling getUser method
public class RestError {
private int code;
private String message;
private String type;
public RestError(int code, String message, String type) {
this.code = code;
this.message = message;
this.type = type;
}
}
Any idea to fix that?
UPDATE:
I added this in the ItemTypeAdatperFactory
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("data")) {
jsonElement = jsonObject.get("data");
} else if (jsonObject.has("error")) {
jsonElement = jsonObject.get("error");
TypeAdapter<RestError> restErrorTypeAdapter = gson.getAdapter(RestError.class);
RestError error = restErrorTypeAdapter.fromJsonTree(jsonElement);
return (T) error;
}
}
return delegate.fromJsonTree(jsonElement);
}
And I created a custom Callback like this:
public abstract static class CustomCallback<T> implements Callback<T> {
public abstract void onError(RestError error);
public abstract void onSuccess(T body);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if(response.body() instanceof RestError) {
onError((RestError) response.body());
} else {
onSuccess(response.body());
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
Log.e(TAG, t.toString());
}
}
So now how the call is:
ServiceAPI.getUser(new ServiceAPI.CustomCallback<User>() {
#Override
public void onError(RestError error) {
Log.e(TAG, error.toString());
}
#Override
public void onSuccess(User body) {
Log.d(TAG, body.toString());
User user = body;
}
#Override
public void onFailure(Call<User> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
So now when an error occurs I get it from onError(), otherwise onSuccess() and seems to do the job
What do you think about?
You can extend all your POJOs from some Response class, which will contain field RestError error. Then you can make some isSuccessful method, which will check is error field null or not and handle it properly.
In my view, you can customize your ItemTypeAdapterFactory like that to check if the reponse is your data or it is an error
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("data")) {
jsonElement = jsonObject.get("data");
} else if(jsonObject.has("error"){
jsonElement = jsonObject.get("error");
}
}
//TODO: need to handle your parsing here (to data or to error)
return delegate.fromJsonTree(jsonElement);
}

Categories

Resources