Search code examples
androidrx-javarx-androidrx-java2

RXjava and Retrofit to handle empty data response


I use Rxjava and Retrofit to handle the response from an HTTP server.

Most of the time, data response is in this format:

{
  code: 'OK',
  data: {.....}
}

And sometimes, the data property doesn't exist:

{
  code: 'OK'
}

I make a BaseResponse class.

public class BaseResponse<T> implements Serializable {
    private String code;
    private String msg;
    private T data;

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }
}

And make a Map function to parse it:

public static <T> Function<BaseResponse<T>, T> applyUnwrapResponse () {
        return new Function<BaseResponse<T>, T>() {
            @Override
            public T apply(@NonNull BaseResponse<T> baseResponse) throws Exception {
                if (baseResponse.getCode().equals(Constants.SUCCESS_CODE)) {
                    // http returned from server
                    T data = baseResponse.getData();
                    if (data == null) {
                        return null;  // throw exception: The mapper function returned a null value
                        return return new T(); // syntax error
                        // how ?
                    }
                    return data;
                } else if (baseResponse.getCode().equals(Constants.USER_NOT_LOGGED_IN)) {
                    // auth token error
                    throw new UserNotLoginException(baseResponse.getMsg());
                }
                // unknown error
                throw new UnknownServerException(baseResponse.getMsg());
            }
        };
    }

When data property doesn't exist, I try to return null, but this is not allowed in Rxjava2.
In the customize Observer, I handle all success scenarios in onNext(), so I need to emit an error when data == null.
but I can not return null, and also cannot return a T by return new T(), so how should I do? Anyone can help me out?


Solution

  • Several suggested options:

    • As Gson parser will put null in fields with no data, so you can return the entire BaseResponse object and check the nullity of data property object at the Subscriber/Observer.
    • You can simply wrap your T value with some Optional alike interface, which can have a value, or can have nothing (Java8 Optional probably not available with android, but you can come up with simple structure like Optional yourself), then on your onNext() you can check whether you have a value or not.

    • RxJava2 has new Observable type called Maybe, which either emit a single item - onSuccess notification, or nothing - onComplete() notification, (and also errors):

      onSubscribe (onSuccess | onCompleted | onError)?  
      

      You can use flatMapMaybe with both Observable or Single (from the Retrofit service interface), to map some response value to a Maybe, in the flatMapMaybe you can use Maybe.fromCallable to return null like you did here, Maybe.fromCallable will treat returned null as no items emitted, and you'll get onComplete(), so you can distinguish between result case (onNext) to the no items case (onComplete).