3

I'm using an API which always returns JSON object that looks like this:

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

data field is a JSON object that holds all valuable information. Of course it's different for different requests. So my retrofit interface looks like this:

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

And when I want to handle response I need to do eg.:

response.getData().getUserId();

I don't really need that boolean success field and I'd like to omit it, so that my retrofit interface could look like this:

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

Is it possible to do that in Gson? Or maybe a neat Rx function which will automatically transform this?

EDIT: Example json:

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

EDIT 2: I've manged to do this with retrofit interceptor where I manually modify response body. It works but if you got any other suggestions, please post them :)

rafakob
  • 3,946
  • 3
  • 26
  • 36

2 Answers2

6

As Than said, solution with the interceptor is not so great. I've managed to solve this with a Rx transformer. I've also added custom api exception that I can throw when something went wrong and easy handle it in onError. I think it's more robust.

Response wrapper:

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

Error object returned when success is false:

public class ApiError {
    private int code;
}

Throw this exception when success is false:

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;
    }
}

And a transformer:

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();
            });
}
rafakob
  • 3,946
  • 3
  • 26
  • 36
1

As far is I know, there is not an easy solution to achieve what you want. By easy solution I mean something what is more reasonable than what you have now. You can deserialize all the data by yourself, you can strip data using interceptors but it's a lot more effort that just using the API the way it's now.

Also, think about what if it changes and some field apperas right next to that item and boolean (for example long last update time), you will have to change everything.

The only reasonable idea I have is to wrap your Api interface with another class and inside it call .map on your Observable to transform ApiResponse<T> into T.

Than
  • 2,759
  • 1
  • 20
  • 41