Repository: square/retrofit
Issue: Using retrofit + okhttp + gson

Hello,

I writed a function to get data from json with okhttp, it work for all class:

```
public class GetDataFromAPI<T> extends AsyncTask<ApiFormat, Void, T> {
    private static final String TAG = "GetDataFromAPI";
    final Class<T> classType;
    public GetDataFromAPI(Class<T> classType){
        this.classType = classType;
    }

    @Override
    protected T doInBackground(ApiFormat... params) {
        String method = params[0].getMethod();
        String url = params[0].getUrl();
        RequestBody requestBody = params[0].getRequestBody();
        if (url == null){
            return null;
        }
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .method(method, requestBody)
                .build();
        Response responses = null;
        InputStream inputStream = null;
        if (isCancelled()){
            return null;
        }
        try {
            responses = client.newCall(request).execute();
            if (responses.code() == 200){
                try {
                    inputStream = responses.body().byteStream();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return load(inputStream, classType);
    }

    private   <T> T load(final InputStream inputStream, final Class<T> clazz) {
        T result = null;
        try {
            if (inputStream != null) {
                final Gson gson = new Gson();
                final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                result =  gson.fromJson(reader, clazz);
                return result;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
```

However, now i want cache json to my app can be work offline. I know retrofit can be config with RequestAdapter (as Request on okhttp). I tried and not success.
Please help me change my function to it work


Issue comments:
GitHub issues are not a support forum but are a place to track bugs and feature requests. Please post to StackOverflow with the 'retrofit' tag which will not only garner more attention for a solution, but serve as an easily searchable place for the question to be archived for others.


Pull request review comments:
(We don't do a great job of overriding `toString()` throughout Retrofit)

Unused import

Unused import


Patch that resolved the issue:
diff --git a/retrofit/src/main/java/retrofit/Converter.java b/retrofit/src/main/java/retrofit/Converter.java
index 5df86878ac..6a82784a80 100644
--- a/retrofit/src/main/java/retrofit/Converter.java
+++ b/retrofit/src/main/java/retrofit/Converter.java
@@ -20,7 +20,11 @@
 import java.io.IOException;
 import java.lang.reflect.Type;
 
-/** Convert objects to and from their representation as HTTP bodies. */
+/**
+ * Convert objects to and from their representation as HTTP bodies. Register a converter with
+ * Retrofit using {@link Retrofit.Builder#addConverter(Type, Converter)} or {@link
+ * Retrofit.Builder#addConverterFactory(Factory)}.
+ */
 public interface Converter<T> {
   /** Convert an HTTP response body to a concrete object of the specified type. */
   T fromBody(ResponseBody body) throws IOException;
diff --git a/retrofit/src/main/java/retrofit/Retrofit.java b/retrofit/src/main/java/retrofit/Retrofit.java
index 3f0561eac0..c28ba5924b 100644
--- a/retrofit/src/main/java/retrofit/Retrofit.java
+++ b/retrofit/src/main/java/retrofit/Retrofit.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -205,6 +206,21 @@ public Builder baseUrl(BaseUrl baseUrl) {
       return this;
     }
 
+    /** Add converter for serialization and deserialization of {@code type}. */
+    public <T> Builder addConverter(final Type type, final Converter<T> converter) {
+      checkNotNull(type, "type == null");
+      checkNotNull(converter, "converter == null");
+      converterFactories.add(new Converter.Factory() {
+        @Override public Converter<?> get(Type candidate) {
+          return candidate.equals(type) ? converter : null;
+        }
+        @Override public String toString() {
+          return "ConverterFactory(type=" + type + ",converter=" + converter + ")";
+        }
+      });
+      return this;
+    }
+
     /** Add converter factory for serialization and deserialization of objects. */
     public Builder addConverterFactory(Converter.Factory converterFactory) {
       converterFactories.add(checkNotNull(converterFactory, "converterFactory == null"));
diff --git a/retrofit/src/main/java/retrofit/http/Part.java b/retrofit/src/main/java/retrofit/http/Part.java
index 154af60868..0f59137775 100644
--- a/retrofit/src/main/java/retrofit/http/Part.java
+++ b/retrofit/src/main/java/retrofit/http/Part.java
@@ -18,7 +18,7 @@
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
-import retrofit.Retrofit;
+import retrofit.Converter;
 
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -31,7 +31,7 @@
  * <li>If the type is {@link String} the value will also be used directly with a {@code text/plain}
  * content type.</li>
  * <li>Other object types will be converted to an appropriate representation by using
- * {@linkplain Retrofit#converterFactory() a converter}.</li>
+ * {@linkplain Converter a converter}.</li>
  * </ul>
  * <p>
  * Values may be {@code null} which will omit them from the request body.
diff --git a/retrofit/src/main/java/retrofit/http/PartMap.java b/retrofit/src/main/java/retrofit/http/PartMap.java
index 512f3545fb..a8d788447b 100644
--- a/retrofit/src/main/java/retrofit/http/PartMap.java
+++ b/retrofit/src/main/java/retrofit/http/PartMap.java
@@ -18,7 +18,7 @@
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
-import retrofit.Retrofit;
+import retrofit.Converter;
 
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -31,7 +31,7 @@
  * <li>If the type is {@link String} the value will also be used directly with a {@code text/plain}
  * content type.</li>
  * <li>Other object types will be converted to an appropriate representation by using
- * {@linkplain Retrofit#converterFactory() a converter}.</li>
+ * {@linkplain Converter a converter}.</li>
  * </ul>
  * <p>
  * <pre>


Test patch:
diff --git a/retrofit/src/test/java/retrofit/RetrofitTest.java b/retrofit/src/test/java/retrofit/RetrofitTest.java
index 6934c6c057..e5a7ea5efd 100644
--- a/retrofit/src/test/java/retrofit/RetrofitTest.java
+++ b/retrofit/src/test/java/retrofit/RetrofitTest.java
@@ -11,6 +11,7 @@
 import java.io.IOException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.math.BigInteger;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -38,6 +39,24 @@
 public final class RetrofitTest {
   @Rule public final MockWebServer server = new MockWebServer();
 
+  static final Converter<BigInteger> bigIntegerConverter = new Converter<BigInteger>() {
+    @Override public BigInteger fromBody(ResponseBody body) throws IOException {
+      return new BigInteger(body.string());
+    }
+    @Override public RequestBody toBody(BigInteger value) {
+      return RequestBody.create(MediaType.parse("text/plain"), value.toString());
+    }
+  };
+  static final Converter<CharSequence> charSequenceConverter = new Converter<CharSequence>() {
+    @Override public CharSequence fromBody(ResponseBody body) throws IOException {
+      return new StringBuilder().append(body.string());
+    }
+
+    @Override public RequestBody toBody(CharSequence value) {
+      return RequestBody.create(MediaType.parse("text/plain"), value.toString());
+    }
+  };
+
   interface CallMethod {
     @GET("/") Call<String> disallowed();
     @POST("/") Call<ResponseBody> disallowed(@Body String body);
@@ -62,6 +81,10 @@ interface Unresolvable {
   interface VoidService {
     @GET("/") void nope();
   }
+  interface CustomConverter {
+    @POST("/a") Call<BigInteger> call(@Body BigInteger bigInteger);
+    @POST("/b") Call<CharSequence> call(@Body CharSequence charSequence);
+  }
 
   @SuppressWarnings("EqualsBetweenInconvertibleTypes") // We are explicitly testing this behavior.
   @Test public void objectMethodsStillWork() {
@@ -244,7 +267,7 @@ class GreetingCallAdapterFactory implements CallAdapter.Factory {
           + "    for method CallMethod.disallowed");
       assertThat(e.getCause()).hasMessage(
           "Could not locate converter for class java.lang.String. Tried:\n"
-              + " * retrofit.RetrofitTest$1\n"
+              + " * retrofit.RetrofitTest$3\n"
               + " * retrofit.OkHttpBodyConverterFactory");
     }
   }
@@ -456,6 +479,43 @@ class GreetingCallAdapterFactory implements CallAdapter.Factory {
     assertThat(retrofit.callAdapterFactories()).isNotEmpty();
   }
 
+  @Test public void addConverter() throws Exception {
+    Retrofit retrofit = new Retrofit.Builder()
+        .baseUrl(server.url("/"))
+        .addConverter(BigInteger.class, bigIntegerConverter)
+        .addConverter(CharSequence.class, charSequenceConverter)
+        .build();
+    CustomConverter api = retrofit.create(CustomConverter.class);
+
+    server.enqueue(new MockResponse().setBody("456"));
+    assertThat(api.call(new BigInteger("123")).execute().body())
+        .isEqualTo(new BigInteger("456"));
+    assertThat(server.takeRequest().getBody().readUtf8()).isEqualTo("123");
+
+    server.enqueue(new MockResponse().setBody("DEF"));
+    assertThat(api.call(new StringBuilder("ABC")).execute().body())
+        .matches("DEF");
+    assertThat(server.takeRequest().getBody().readUtf8()).isEqualTo("ABC");
+  }
+
+  @Test public void addConverterNullType() throws Exception {
+    try {
+      new Retrofit.Builder().addConverter(null, bigIntegerConverter);
+      fail();
+    } catch (NullPointerException expected) {
+      assertThat(expected).hasMessage("type == null");
+    }
+  }
+
+  @Test public void addConverterNullConverter() throws Exception {
+    try {
+      new Retrofit.Builder().addConverter(BigInteger.class, null);
+      fail();
+    } catch (NullPointerException expected) {
+      assertThat(expected).hasMessage("converter == null");
+    }
+  }
+
   @Test public void callAdapterFactoryPropagated() {
     CallAdapter.Factory factory = mock(CallAdapter.Factory.class);
     Retrofit retrofit = new Retrofit.Builder()