Repository: square/retrofit
Issue: How can I add convertor to retrofit 2.0 with a generic gson?

my server json like this:
{code:
msg:
data:{}
}

in POJO, data is generic (private T data;),I know how to parse,but I don't know how to add gson convertor to retrofit2.0?


Issue comments:
### Read the official document

[]()http://square.github.io/retrofit/

> CONVERTERS
> 
> By default, Retrofit can only deserialize HTTP bodies into OkHttp's ResponseBody type and it can only accept its RequestBody type for @Body.
> 
> Converters can be added to support other types. Six sibling modules adapt popular serialization libraries for your convenience.
> 
> Gson: com.squareup.retrofit2:converter-gson
> Jackson: com.squareup.retrofit2:converter-jackson
> Moshi: com.squareup.retrofit2:converter-moshi
> Protobuf: com.squareup.retrofit2:converter-protobuf
> Wire: com.squareup.retrofit2:converter-wire
> Simple XML: com.squareup.retrofit2:converter-simplexml
> Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

### About Serializing and Deserializing Generic Types：

Assumed, Serializing and Deserializing json data with Gson
[](url)https://github.com/google/gson/blob/master/UserGuide.md#TOC-Serializing-and-Deserializing-Generic-Types

@yy197133 
It seams that you are not reading the retrofit document carefully. Always look at the official document/example before ask any questions;

```
I don't know how to add gson convertor to retrofit2.0?
```

Use com.squareup.retrofit2:converter-gson mentioned above;

```
data is generic (private T data;)
```

Data is absolutely ok to be different for each API response, it‘s nothing to do with generic, you'd better specify the detail data structure for each response. You can define base response class with data to be generic

Seems like this was answered. Additionally there's a `samples/` folder which has some example usages that include Gson. Feel free to direct specific questions to StackOverflow with the 'retrofit' tag in the future.


Pull request review comments:

Patch that resolved the issue:
diff --git a/pom.xml b/pom.xml
index b99efb76b1..9336f3a3df 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,7 @@
     <rxjava.version>1.1.1</rxjava.version>
 
     <!-- Converter Dependencies -->
+    <fastjson.version>1.2.8</fastjson.version>
     <gson.version>2.6.1</gson.version>
     <protobuf.version>2.6.1</protobuf.version>
     <jackson.version>2.7.2</jackson.version>
@@ -115,6 +116,11 @@
         <artifactId>okhttp</artifactId>
         <version>${okhttp.version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.alibaba</groupId>
+        <artifactId>fastjson</artifactId>
+        <version>${fastjson.version}</version>
+      </dependency>
       <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
diff --git a/retrofit-converters/fastjson/README.md b/retrofit-converters/fastjson/README.md
new file mode 100644
index 0000000000..238b51e36c
--- /dev/null
+++ b/retrofit-converters/fastjson/README.md
@@ -0,0 +1,6 @@
+FastJson Converter
+==============
+
+A `Converter` which uses [FastJson][1] for serialization to and from JSON.
+
+ [1]: https://github.com/alibaba/fastjson
diff --git a/retrofit-converters/fastjson/pom.xml b/retrofit-converters/fastjson/pom.xml
new file mode 100644
index 0000000000..25cb8cc306
--- /dev/null
+++ b/retrofit-converters/fastjson/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.squareup.retrofit2</groupId>
+        <artifactId>retrofit-converters</artifactId>
+        <version>2.0.3-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>converter-fastjson</artifactId>
+    <name>Converter: FastJson</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>retrofit</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>mockwebserver</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonConverterFactory.java b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonConverterFactory.java
new file mode 100644
index 0000000000..9ed0f2b391
--- /dev/null
+++ b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonConverterFactory.java
@@ -0,0 +1,100 @@
+package retrofit2.converter.fastjson;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.parser.deserializer.ParseProcess;
+import com.alibaba.fastjson.serializer.SerializeConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import okhttp3.RequestBody;
+import okhttp3.ResponseBody;
+import retrofit2.Converter;
+import retrofit2.Retrofit;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+
+/**
+ * A {@linkplain Converter.Factory converter} which uses FastJson for JSON.
+ * <p>
+ * Because FastJson is so flexible in the types it supports, this converter assumes that it can handle
+ * all types. If you are mixing JSON serialization with something else (such as protocol buffers),
+ * you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
+ * last to allow the other converters a chance to see their types.
+ */
+public class FastJsonConverterFactory extends Converter.Factory {
+
+    private ParserConfig mParserConfig = ParserConfig.getGlobalInstance();
+    private int mFeatureValues = JSON.DEFAULT_PARSER_FEATURE;
+    private Feature[] mFeatures;
+
+    private SerializeConfig mSerializeConfig;
+    private SerializerFeature[] mSerializerFeatures;
+
+    /**
+     * Create an default instance for conversion. Encoding to JSON and
+     * decoding from JSON (when no charset is specified by a header) will use UTF-8.
+     */
+    public static FastJsonConverterFactory create() {
+        return new FastJsonConverterFactory();
+    }
+
+    private FastJsonConverterFactory() {
+    }
+
+    @Override
+    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
+        return new FastJsonResponseBodyConverter<>(type, mParserConfig, mFeatureValues, mFeatures);
+    }
+
+    @Override
+    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
+        return new FastJsonRequestBodyConverter<>(mSerializeConfig, mSerializerFeatures);
+    }
+
+    public ParserConfig getParserConfig() {
+        return mParserConfig;
+    }
+
+    public FastJsonConverterFactory setParserConfig(ParserConfig config) {
+        this.mParserConfig = config;
+        return this;
+    }
+
+    public int getParserFeatureValues() {
+        return mFeatureValues;
+    }
+
+    public FastJsonConverterFactory setParserFeatureValues(int featureValues) {
+        this.mFeatureValues = featureValues;
+        return this;
+    }
+
+    public Feature[] getParserFeatures() {
+        return mFeatures;
+    }
+
+    public FastJsonConverterFactory setParserFeatures(Feature[] features) {
+        this.mFeatures = features;
+        return this;
+    }
+
+    public SerializeConfig getSerializeConfig() {
+        return mSerializeConfig;
+    }
+
+    public FastJsonConverterFactory setSerializeConfig(SerializeConfig serializeConfig) {
+        this.mSerializeConfig = serializeConfig;
+        return this;
+    }
+
+    public SerializerFeature[] getSerializerFeatures() {
+        return mSerializerFeatures;
+    }
+
+    public FastJsonConverterFactory setSerializerFeatures(SerializerFeature[] serializerFeatures) {
+        this.mSerializerFeatures = serializerFeatures;
+        return this;
+    }
+}
diff --git a/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonRequestBodyConverter.java b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonRequestBodyConverter.java
new file mode 100644
index 0000000000..8432de8ffd
--- /dev/null
+++ b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonRequestBodyConverter.java
@@ -0,0 +1,40 @@
+package retrofit2.converter.fastjson;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializeConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+import retrofit2.Converter;
+
+import java.io.IOException;
+
+final class FastJsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
+    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
+    private SerializeConfig mSerializeConfig;
+    private SerializerFeature[] mSerializerFeatures;
+
+    FastJsonRequestBodyConverter(SerializeConfig config, SerializerFeature... features) {
+        mSerializeConfig = config;
+        mSerializerFeatures = features;
+    }
+
+    @Override
+    public RequestBody convert(T value) throws IOException {
+        byte[] content;
+        if (mSerializeConfig != null) {
+            if (mSerializerFeatures != null) {
+                content = JSON.toJSONBytes(value, mSerializeConfig, mSerializerFeatures);
+            } else {
+                content = JSON.toJSONBytes(value, mSerializeConfig);
+            }
+        } else {
+            if (mSerializerFeatures != null) {
+                content = JSON.toJSONBytes(value, mSerializerFeatures);
+            } else {
+                content = JSON.toJSONBytes(value);
+            }
+        }
+        return RequestBody.create(MEDIA_TYPE, content);
+    }
+}
diff --git a/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonResponseBodyConverter.java b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonResponseBodyConverter.java
new file mode 100644
index 0000000000..193112ba44
--- /dev/null
+++ b/retrofit-converters/fastjson/src/main/java/retrofit2/converter/fastjson/FastJsonResponseBodyConverter.java
@@ -0,0 +1,40 @@
+package retrofit2.converter.fastjson;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.parser.deserializer.ParseProcess;
+import okhttp3.ResponseBody;
+import retrofit2.Converter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+final class FastJsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
+
+    private static final Feature[] EMPTY_SERIALIZER_FEATURES = new Feature[0];
+
+    private Type mType;
+
+    private ParserConfig mConfig;
+    private int mFeatureValues;
+    private Feature[] mFeatures;
+
+    FastJsonResponseBodyConverter(Type type, ParserConfig config, int featureValues,
+                                         Feature... features) {
+        mType = type;
+        mConfig = config;
+        mFeatureValues = featureValues;
+        mFeatures = features;
+    }
+
+    @Override
+    public T convert(ResponseBody value) throws IOException {
+        try {
+            return JSON.parseObject(value.string(), mType, mConfig, mFeatureValues,
+                    mFeatures != null ? mFeatures : EMPTY_SERIALIZER_FEATURES);
+        } finally {
+            value.close();
+        }
+    }
+}
diff --git a/retrofit-converters/pom.xml b/retrofit-converters/pom.xml
index 87b1463cc2..b4c89fb8b7 100644
--- a/retrofit-converters/pom.xml
+++ b/retrofit-converters/pom.xml
@@ -22,5 +22,6 @@
     <module>simplexml</module>
     <module>scalars</module>
     <module>moshi</module>
+    <module>fastjson</module>
   </modules>
 </project>


Test patch:
diff --git a/retrofit-converters/fastjson/src/test/java/retrofit2/converter/fastjson/FastJsonConverterFactoryTest.java b/retrofit-converters/fastjson/src/test/java/retrofit2/converter/fastjson/FastJsonConverterFactoryTest.java
new file mode 100644
index 0000000000..8d1a245a14
--- /dev/null
+++ b/retrofit-converters/fastjson/src/test/java/retrofit2/converter/fastjson/FastJsonConverterFactoryTest.java
@@ -0,0 +1,116 @@
+package retrofit2.converter.fastjson;
+
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
+import com.alibaba.fastjson.serializer.JSONSerializer;
+import com.alibaba.fastjson.serializer.ObjectSerializer;
+import com.alibaba.fastjson.serializer.SerializeWriter;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import retrofit2.Call;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.http.Body;
+import retrofit2.http.POST;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FastJsonConverterFactoryTest {
+    interface AnInterface {
+        @JSONField(name = "name")
+        String getName();
+        @JSONField(name = "name")
+        void setName(String name);
+    }
+
+    static class AnImplementation implements AnInterface {
+
+        @JSONField(name = "name")
+        private String theName;
+
+        AnImplementation() {
+        }
+
+        AnImplementation(String name) {
+            theName = name;
+        }
+
+        @JSONField(name = "name")
+        @Override public String getName() {
+            return theName;
+        }
+
+        @JSONField(name = "name")
+        @Override
+        public void setName(String name) {
+            theName = name;
+        }
+    }
+
+    interface Service {
+        @POST("/")
+        Call<AnImplementation> anImplementation(@Body AnImplementation impl);
+        @POST("/") Call<AnInterface> anInterface(@Body AnInterface impl);
+    }
+
+    @Rule
+    public final MockWebServer server = new MockWebServer();
+
+    private Service service;
+
+    @Before
+    public void setUp() {
+        Retrofit retrofit = new Retrofit.Builder()
+                .baseUrl(server.url("/"))
+                .addConverterFactory(FastJsonConverterFactory.create())
+                .build();
+        service = retrofit.create(Service.class);
+    }
+
+    @Test
+    public void anInterface() throws IOException, InterruptedException {
+        server.enqueue(new MockResponse().setBody("{\"name\":\"value\"}"));
+
+        Call<AnInterface> call = service.anInterface(new AnImplementation("value"));
+        Response<AnInterface> response = call.execute();
+        AnInterface body = response.body();
+        assertThat(body.getName()).isEqualTo("value");
+
+        RecordedRequest request = server.takeRequest();
+        assertThat(request.getBody().readUtf8()).isEqualTo("{\"name\":\"value\"}");
+        assertThat(request.getHeader("Content-Type")).isEqualTo("application/json; charset=UTF-8");
+    }
+
+    @Test public void anImplementation() throws IOException, InterruptedException {
+        server.enqueue(new MockResponse().setBody("{\"theName\":\"value\"}"));
+
+        Call<AnImplementation> call = service.anImplementation(new AnImplementation("value"));
+        Response<AnImplementation> response = call.execute();
+        AnImplementation body = response.body();
+        assertThat(body.theName).isNull();
+
+        RecordedRequest request = server.takeRequest();
+        assertThat(request.getBody().readUtf8()).isEqualTo("{\"name\":\"value\"}");
+        assertThat(request.getHeader("Content-Type")).isEqualTo("application/json; charset=UTF-8");
+    }
+
+    @Test public void serializeUsesConfiguration() throws IOException, InterruptedException {
+        server.enqueue(new MockResponse().setBody("{}"));
+
+        service.anImplementation(new AnImplementation(null)).execute();
+
+        RecordedRequest request = server.takeRequest();
+        assertThat(request.getBody().readUtf8()).isEqualTo("{}"); // Null value was not serialized.
+        assertThat(request.getHeader("Content-Type")).isEqualTo("application/json; charset=UTF-8");
+    }
+
+}