Retrofit Converter for wrapped responses

Written by vickyturtle | Published 2017/05/06
Tech Story Tags: android | retrofit2 | json | retrofit

TLDRvia the TL;DR App

Many a times API responses are wrapped inside a wrapper class with custom status codes inside the responses. One of the common formats being the following

{"data" : {...}"status_code" : 0"error_message" : null}

If we use the prepackaged json converter (gson/jackson) for retrofit we would have to extract the underlying data object everywhere we make the api calls. This leads to duplication of the data object extraction and error check logic. Something like following

data class Data<T>(var statusCode: Int = 0,var data: T? = null,var message: String? = null)

interface Api {@POST("user/login/")fun login(@Body request: LoginRequest): Single<Data<User>>}

class UserManager {fun login(userName: String, password: String){api.login(LoginRequest(userName, password)).subscribeOn(schedulerProvider.getIoScheduler()).observeOn(schedulerProvider.getUiScheduler()).map {if(it.statusCode ==0) then response.data else throw ApiException(it.error_message)}.subscribe{...}}...

}

Thankfully we can use our own custom converter for retrofit, which would just be a wrapper above the usual Gson Converter, such that this data extraction and error handling logic is handled at one place only. We basically need two classes

  • CustomConverter which implements the Retrofit’s Converter<F,T> interface.
  • CustomConverterFactory which implements the Retrofit’s Converter#Factory interface. This is responsible for creating CustomConverters for each types.

In the above gist we are using the actual converters created by `GsonConverterFactory` only to parse the response, so we don’t have to rewrite any json marshalling code. Our CustomConverter just parses the wrapped class by providing the wrapped `Type` instead of the actual `Type`.

interface Api {@POST("user/login/")fun login(@Body request: LoginRequest): Single<User>}

class UserManager {fun login(userName: String, password: String){api.login(LoginRequest(userName, password)).subscribeOn(schedulerProvider.getIoScheduler()).observeOn(schedulerProvider.getUiScheduler()).subscribe{...}}...

}

This is how our Api interface and logic look like now. Notice the change in return type of the Api interface login method.

Note: This converter assumes that all you responses are in the wrapped format and will fail for any response that comes in unwrapped format


Published by HackerNoon on 2017/05/06