简体   繁体   中英

Retrofit multiple response types

How can I use Retrofit2 to parse these two kinds of API responses?

Ok response (HTTP 200):

{
    "data": {
        "foo": "bar"
    }
}

Error response (HTTP 200):

{
    "error": {
        "foo": "bar"
    }
}

I've read tons of SO questions and tutorials, but I don't know how to do that, I've tried:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
Gson gson = gsonBuilder.create();

final Retrofit retrofit = new Retrofit.Builder()
        .client(getOkHttpClient())
        .baseUrl(Constants.API_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();

And this is my ItemTypeAdapterFactory:

class ItemTypeAdapterFactory implements TypeAdapterFactory {

    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {

        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            public T read(JsonReader in) throws IOException {

                JsonElement jsonElement = elementAdapter.read(in);

                if (jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();

                    // Data key
                    if (jsonObject.has(Constants.JSON_KEY_DATA)) {

                        JsonElement jsonData = jsonObject.get(Constants.JSON_KEY_DATA);

                        // Primitive
                        if (jsonData.isJsonPrimitive()) {
                            jsonElement = jsonData.getAsJsonPrimitive();
                        }
                        // JSON object
                        else if (jsonData.isJsonObject()) {
                            jsonElement = jsonData;
                        }
                        // JSON object array
                        else if (jsonData.isJsonArray()) {
                            jsonElement = jsonData.getAsJsonArray();
                        }
                    }
                }

                return delegate.fromJsonTree(jsonElement);
            }
        }.nullSafe();
    }
}

But now I don't know the type to be declared on retrofit2 interface, inside Call:

@GET("login")
Call<?> login(@Query(Constants.API_PARAM_TOKEN) String token);

Could you please point me in the right direction?

In a similar case, I once used JsonObject as type, so your function will look like this:

@GET("login")
Call<?> login(@Query(Constants.API_PARAM_TOKEN) String token);

Next, when you make a retrofit call, you keep the response as a string. So, in your java code, do something like this:

Call<JsonObject> call = RetrofitClient.getAPIService().login('YOUR_INPUT');
Data data = null;
Error error = null;
call.enqueue(new Callback<JsonObject>() {
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
if(response.isSuccessfull()){
  String jsonString = response.body().toString();
  if(jsonString.contains("data:")){
       data = new Gson().fromJson(jsonString,Data.class);
  }else{
      error = new Gson().fromJson(jsonString,Error.class);
  }
}
        }

Here, I have used Data and Error these 2 classes. They are the POJOs. So Data can look something like this:

Data.java :

public class Data implements Serializable{
 @SerializedName("foo")
    @Expose
    private Foo foo; // Foo is your desired data type 

}

Same goes for Error . So depending on your rest of the code, make necessary changes. Good luck.

I used to do something like this
BaseResponse

public class BaseResponse<D,E>{
E error;
D data;
public boolean isSuccess(){
    return error==null;
}

}

Retrofit interface

@GET("login")
Call<BaseResponse<LoginData,ErrorData>> login(@Query(Constants.API_PARAM_TOKEN) String token);

this approach will work OK when you have control over the REST API structure.
the only problem is that you need to check for success using isSuccess method for every request before using the data object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM