Android Retrofit custom GsonConverterFactory

During project development, the format returned by the background does not have a unified return format (the return formats of success and failure are inconsistent).
Case description:
Login business json (when successful)

{
    "code": 0,
    "message": "Login successful",
    "data": {
        "name": "admin",
        "token": "ad987810544564310"
    }
}

Login business json (when failed)

{
    "code": 0,
    "message": "Login successful",
    "data": ""
}

Attentive friends may have discovered the problem. Object is returned on success, and String is returned on failure. This will cause json parsing exception. Our general configurationRetrofitis as follows.

 Retrofit retrofit = new Retrofit.Builder()
                .client(getOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();

The GsonConverterFactory class fails to parse, which will cause an error. So we need to customize the GsonConverterFactory class to solve the above problem.

1. MyGsonConverterFactory class
public class MyGsonConverterFactory extends Converter.Factory {

  public static MyGsonConverterFactory create() {
    return create(new Gson());
  }


  @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
  public static MyGsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new MyGsonConverterFactory(gson);
  }

  private final Gson gson;

  private MyGsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new MyGsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new MyGsonRequestBodyConverter<>(gson, adapter);
  }
}
2. MyGsonRequestBodyConverter class
public class MyGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
  private static final Charset UTF_8 = Charset.forName("UTF-8");

  private final Gson gson;
  private final TypeAdapter<T> adapter;

  MyGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    JsonWriter jsonWriter = gson.newJsonWriter(writer);
    adapter.write(jsonWriter, value);
    jsonWriter.close();
    return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
  }
}
3. MyGsonResponseBodyConverter class
//Method 1
public class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final Gson gson;
    private final TypeAdapter<T> adapter;

    MyGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {

        String json = value.string();
        //first analysis
        BaseBean obj = GsonUtils.json2Bean(json, BaseBean.class);
        if (!obj.isSuccess()) {
            //If it is the error code returned by the server, throw a custom exception
            throw new ApiException(obj.getStatus(), obj.getMsg());
        }
        //Second parsing
        T result = adapter.fromJson(json);
        value.close();
        return result;


        //Native code (code in RxJava)
// JsonReader jsonReader = gson.newJsonReader(value.charStream());
// try {
// T result = adapter.read(jsonReader);
// if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
// throw new JsonIOException("JSON document was not fully consumed.");
// }
// return result;
// } finally {
// value.close();
// }
    }
}
//Method 2
public class BaseResponse{
    public int code;
    public String msg;
}

final class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {

    private final TypeAdapter<T> adapter;

    GsonResponseBodyConverter(TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public Object convert(ResponseBody value) throws IOException {
        try {
            BaseResponse response = (BaseResponse) adapter.fromJson(value.charStream());
            if (response.getCode() == 200) {
                //success
                return response.getResults();
            } else {
                // Specific API errors are handled in the corresponding onError method.
                throw new ApiException(response.getCode(), response.getMessage());
            }
        } finally {
            value.close();
        }

        return null;
    }
}
Calling method
 Retrofit retrofit = new Retrofit.Builder()
                .client(getOkHttpClient())
// .addConverterFactory(GsonConverterFactory.create()) //Replace the previous one, just use the following method
                .addConverterFactory(MyGsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
Attached is also the json parsing tool class:
public class GsonUtils {

    private static Gson mGson = new Gson();

    public static <T> T json2Bean(String json, Class<T> clazz) {
        try {
            return mGson.fromJson(json, clazz);
        } catch (Exception e) {
            LogUtils.e("GsonUtils:" + e.toString());
            return null;
        }
    }
 }

—————-
Reprinted at: https://blog.csdn.net/weixin_42273922/article/details/105947197