Retrofit2处理Api多响应时出现类型转换异常问题
我看你遇到的问题是Retrofit调用API后,明明日志里能打印出成功的响应,但判断response.body() instanceof OtpSuccessResponse却不生效,还抛出了ClassCastException——这是因为你用了Call<Object>作为请求类型,Gson没办法知道要把JSON解析成你的OtpSuccessResponse类,只能默认转成LinkedTreeMap来存储键值对,自然过不了instanceof的判断,强转就报错了。
下面给你两种可行的解决方案:
方案一:使用通用响应类(推荐)
创建一个包含成功和失败所有字段的通用响应类,这样Retrofit可以直接解析成这个类,然后通过status字段来区分处理:
第一步:定义通用响应POJO
public class OtpResponse { @SerializedName("status") @Expose private String status; @SerializedName("message") @Expose private String message; @SerializedName("token") @Expose private String token; @SerializedName("subscription_active") @Expose private String subscriptionActive; @SerializedName("errors") @Expose private List<String> errors; // 生成所有字段的getter方法,方便后续取值 public String getStatus() { return status; } public String getMessage() { return message; } public String getToken() { return token; } public String getSubscriptionActive() { return subscriptionActive; } public List<String> getErrors() { return errors; } }
第二步:修改Retrofit请求和回调
把Call<Object>改成Call<OtpResponse>,然后在回调里根据status判断是成功还是失败:
Call<OtpResponse> call = jsonApi.hitOtp(otpPost); call.enqueue(new Callback<OtpResponse>() { @Override public void onResponse(Call<OtpResponse> call, Response<OtpResponse> response) { Log.d(TAG, "onResponse: "+response.body()); OtpResponse otpResponse = response.body(); if(otpResponse != null){ // 假设成功时status是"success",根据你的实际API返回值调整 if("success".equals(otpResponse.getStatus())){ Toast.makeText(OtpActivity.this, otpResponse.getMessage(), Toast.LENGTH_SHORT).show(); // 这里可以直接获取token和subscription_active String token = otpResponse.getToken(); String isActive = otpResponse.getSubscriptionActive(); } else if("failed".equals(otpResponse.getStatus())){ Toast.makeText(OtpActivity.this, otpResponse.getMessage(), Toast.LENGTH_SHORT).show(); // 处理错误列表 List<String> errors = otpResponse.getErrors(); } } } @Override public void onFailure(Call<OtpResponse> call, Throwable t) { Log.d(TAG, "onFailure: "+t.getMessage()); } });
方案二:手动解析ResponseBody
如果不想用通用类,可以让Retrofit返回ResponseBody,然后手动用Gson解析成对应的POJO:
第一步:定义失败响应POJO
先给失败的响应也创建一个POJO类:
public class OtpFailureResponse { @SerializedName("status") @Expose private String status; @SerializedName("message") @Expose private String message; @SerializedName("errors") @Expose private List<String> errors; public String getMessage() { return message; } public List<String> getErrors() { return errors; } }
第二步:修改请求和回调逻辑
把请求改成Call<ResponseBody>,然后在回调里手动解析JSON:
Call<ResponseBody> call = jsonApi.hitOtp(otpPost); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try { if(response.body() == null) return; String jsonResponse = response.body().string(); Log.d(TAG, "onResponse: "+jsonResponse); // 先解析status字段判断响应类型 JsonObject jsonObj = new JsonParser().parse(jsonResponse).getAsJsonObject(); String status = jsonObj.get("status").getAsString(); if(!"failed".equals(status)){ // 解析成成功响应类 OtpSuccessResponse successRes = new Gson().fromJson(jsonResponse, OtpSuccessResponse.class); Toast.makeText(OtpActivity.this, successRes.getMessage(), Toast.LENGTH_SHORT).show(); } else { // 解析成失败响应类 OtpFailureResponse failureRes = new Gson().fromJson(jsonResponse, OtpFailureResponse.class); Toast.makeText(OtpActivity.this, failureRes.getMessage(), Toast.LENGTH_SHORT).show(); } } catch (IOException e) { e.printStackTrace(); Toast.makeText(OtpActivity.this, "解析响应失败", Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.d(TAG, "onFailure: "+t.getMessage()); } });
为什么原来的代码会报错?
当你使用Call<Object>时,Retrofit的Gson转换器没有收到明确的目标类型信息,它会把JSON对象默认转换成LinkedTreeMap(Gson用来存储无类型JSON对象的默认实现),所以response.body()实际是LinkedTreeMap的实例,不是你定义的OtpSuccessResponse,这就导致instanceof判断返回false,如果你后续尝试强转就会抛出ClassCastException。
内容的提问来源于stack exchange,提问作者Vivek Negi

