简体   繁体   English

改造:Json解析器根据响应

[英]Retrofit: Json parser according to response

I have a REST service and it's response can be change according to status. 我有一个REST服务,它的响应可以根据状态进行更改。 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? 如果“状态”成功,则将JSON响应转换为UserModel,否则将json响应转换为FailureModel?

Retrofit dependency : implementation 'com.squareup.retrofit:retrofit:1.9.0' 改造依赖项:实现'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. 使用自定义json反序列化器是可能的。 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. 如果您遇到状态错误,请尝试访问其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/ 您可以从http://www.jsonschema2pojo.org/获得您的pojos

Pojo's POJO的

Response.java 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 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. 进行2.0改造时,最好的方法是使用Gson转换器。 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. 只需使用可选的json键(针对您的用户和原因)添加@Nullable批注,即可在解析时不会崩溃或不会生成nullpointer异常。 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. 使用android studio插件DTO Genrater创建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, 然后打电话给Retrofit,

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. 如果您的问题是字段可以是一种或另一种,那么您将需要一个转换器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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