Retrofit 2 RxJava - Gson - "Global"反序列化,改变响应类型

Retrofit 2 RxJava - Gson - "Global" deserialization, change response type

我使用的 API 总是 returns JSON 对象,看起来像这样:

public class ApiResponse<T> {
    public boolean success;
    public T data;
}

data 字段是一个 JSON 对象,其中包含所有有价值的信息。当然对于不同的要求是不同的。所以我的改造界面是这样的:

@GET(...)
Observable<ApiResponse<User>> getUser();

当我想处理响应时,我需要做例如:

response.getData().getUserId();

我真的不需要那个 boolean success 字段,我想省略它,这样我的改造界面看起来像这样:

@GET(...)
Observable<User> getUser();

在 Gson 中可以这样做吗?或者也许是一个简洁的 Rx 函数可以自动转换它?

编辑: 例子 json:

{
  "success": true,
  "data": {
    "id": 22,
    "firstname": "Jon",
    "lastname": "Snow"
  }
}

编辑 2: 我已经使用改造拦截器手动修改响应正文来做到这一点。它有效,但如果您有任何其他建议,请 post 他们 :)

据我所知,没有一个简单的解决方案可以实现您想要的。简单的解决方案是指比您现在拥有的更合理的解决方案。您可以自己反序列化所有数据,您可以使用拦截器剥离数据,但仅使用 API 现在的方式需要付出更多的努力。

此外,考虑一下如果它发生变化并且该项目和布尔值旁边的一些字段应用程序(例如 long 上次更新时间),您将不得不更改所有内容。

我唯一合理的想法是用另一个 class 包装你的 Api 接口,并在其中调用 Observable 上的 .map 来转换 ApiResponse<T> 变成 T

正如 Than 所说,拦截器的解决方案不是很好。我已经设法用 Rx 变压器解决了这个问题。我还添加了自定义 api 异常,当出现问题时我可以抛出该异常并在 onError 中轻松处理它。我认为它更强大。

响应包装器:

public class ApiResponse<T> {
    private boolean success;
    private T data;
    private ApiError error;
}

成功为false时返回的错误对象:

public class ApiError {
    private int code;
}

成功为假时抛出异常:

public class ApiException extends RuntimeException {
    private final ApiError apiError;
    private final transient ApiResponse<?> response;

    public ApiException(ApiResponse<?> response) {
        this.apiError = response.getError();
        this.response = response;
    }

    public ApiError getApiError() {
        return apiError;
    }

    public ApiResponse<?> getResponse() {
        return response;
    }
}

还有一个变压器:

protected <T> Observable.Transformer<ApiResponse<T>, T> applySchedulersAndExtractData() {
    return observable -> observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .map(tApiResponse -> {
                if (!tApiResponse.isSuccess())
                    throw new ApiException(tApiResponse);
                else
                    return tApiResponse.getData();
            });
}