简体   繁体   中英

Retrofit: Json parser according to response

I have a REST service and it's response can be change according to status. For example; When I send a request and the response can be two types. The first one like that

{
  "status": "success",
  "user": {
    "user_id": 3554,
    "full_name": "test",
    "email_address": "test@test1.com",
    "end_date": null
  }
}

The second type is like that

{
  "status": "failure",
  "reason": "email_taken"
}

The response according to "status" which comes with response. I searched this problem and find some solutions (custom converter, set custom converter etc.) But I think these are not clear enough . Are there any solution like that; if "status" is success, convert to json response to User model, else convert json response to FailureModel?

Retrofit dependency : implementation 'com.squareup.retrofit:retrofit:1.9.0'

If the only solution is custom converter, please explain it clearly because I am really new on this topic.

You can use a unique model and handle both cases with it :

public class UserResponseModel{

    private String status;
    private String reason;
    private UserModel user;

    // getter/setter

    boolean isFailure(){
        return status == "failure"
    }
    boolean isSuccess(){
        return status == "success"
    }
}

you may then do

UserResponseModel response
if( response.isSuccess() ) // do whatever with response.user
else // do whatever with response.reason

It is possible with custom json deserializer. You only have user when the status is success in case it is not you have the reason. In case you have status error and try to access user its null.

public class CustomConvertor implements JsonDeserializer<Response> {

    @Override
    public Response deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        Gson gson = new Gson();
        Response response = gson.fromJson(json, Response.class);

        if (response.getStatus().equals("success")) {
            // The full response as a json object
            final JsonObject jsonObject = json.getAsJsonObject();
            // The user attribute in the JSON received
            final JsonElement jsonElement = jsonObject.get("user");

            User user = gson.fromJson(jsonElement, User.class);
            response.setUser(user);
        }else{
            // you could do this
            // not needed as json is deserialized to Response already
            // just for the example
            final JsonObject jsonObject = json.getAsJsonObject();
            String reason = jsonObject.getAsJsonPrimitive("reason").getAsString();
            response.setReason(reason);
        }

        return response;
    }
}

The retrofit part

GsonBuilder gsonBuilder =new  GsonBuilder();
gsonBuilder.registerTypeAdapter(Response.class, new CustomConvertor());
Gson gson = gsonBuilder.create();
GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(gson);
Retrofit retrofit = new Retrofit.Builder()
         ...// other setups
        .addConverterFactory(gsonConverterFactory).build();

Then

// service is my case
Service service = retrofit.create(Service.class);
// call enqueue in your case.for testing i used mockwebserver
Response response = service.exampleJson().execute().body();
Log.i("User: ","" + response.geUser().getFullname());

in case of error

Log.i("Error: ","" + response.getReason());

You can get your pojos from http://www.jsonschema2pojo.org/

Pojo's

Response.java

public class Response {

    @SerializedName("status")
    @Expose
    private String status;
    @SerializedName("user")
    @Expose
    private User user;

    @Expose
    @SerializedName("reason")
    private String reason;

    public void setReason(String reason) {
        this.reason = reason;
    }

    public String getReason() {
        return reason;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }    
}

User.java

public class User {

    @SerializedName("user_id")
    @Expose
    private int userId;
    @SerializedName("full_name")
    @Expose
    private String fullName;
    @SerializedName("email_address")
    @Expose
    private String emailAddress;
    @SerializedName("end_date")
    @Expose
    private Object endDate;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public Object getEndDate() {
        return endDate;
    }

    public void setEndDate(Object endDate) {
        this.endDate = endDate;
    }
}

The other way

Call<Response> auth = .// setup
        auth.enqueue(new Callback<Response>() {
            @Override
            public void onResponse(Call<Response> call, Response<Response> response) {
                if (response.isSuccessful() ) {
                      Response respojo = response.body();
                     if(respojo.getStatus().equals("success"){
                         Log.i("User: ","" + respojo.getUser().getFullname());
                      }else {
                         Log.i("Error: ","" + respojo.getReason());
                      }  
                    }
                } else {
                   response.errorBody(); 

                }
            }

            @Override
            public void onFailure(Call<Response> call, Throwable t) {
                t.printStackTrace();
            }
        });

With your retrofit 2.0 best idea is to use Gson converter. Just add @Nullable annotation with your optional json key (in your case user and reason) so it does not crash while parsing or does not generate nullpointer exception. So your model class look like as follows.

public class YourModelClass {

    @SerializedName("status")
    @Expose
    public String status;
    @Nullable
    @SerializedName("user")
    @Expose
    public User user;

    @Nullable
    @SerializedName("reason")
    @Expose
    public String reason;

    public class User {

    @SerializedName("user_id")
    @Expose
    public Integer userId;
    @SerializedName("full_name")
    @Expose
    public String fullName;
    @SerializedName("email_address")
    @Expose
    public String emailAddress;
    @SerializedName("end_date")
    @Expose
    public Object endDate;

    }
}

In your Activity or fragment where you are requesting parse it as follows

@Override
    public void onResponse(Call<YourModelClass> call, Response<YourModelClass> response) {
        if(response.body.yourModelClass.status.equals("succ")) {
            User changesList = response.body().user;
            //perform action with user data
        } else {
           Log.d("failer", response.body().reason)
        }
    }

    @Override
    public void onFailure(Call<YourModelClass> call, Throwable t) {
        t.printStackTrace();
    }

I hope its work for you. Use android studio plugin DTO Genrater for creating pojo.

You can have an englobing class for this, for example:

public class Foo {
    String    status;
    String    reason;
    UserModel user;
    // Constructors, getter/setter, others
    // ...
}

then call Retrofit like,

Call<Foo> callToYourAPI();

and when you want to have a user:

if (foo.reason == "success") // or if (foo.user != null)
    // do something with foo.user

The conversion is done automatically in such a case. If your problem was having a field that can be of one type or another, you would have needed a converter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM