2

I have this responsebody

{
  "status":"OK",
  "payload":{}
}

where payload is generic (a java-representation would look like this)

There was the converter stuff for Retrofit1, but Retrofit2 doesn't seem to allow you to do that generically, only type-specific, which is not an option, when you have 70+ different response-bodies.


Edit:

Since there seems to be confusion here, I'm not looking to convert json-strings into objects.

When I get the aforementioned ResponseBody, I want to be able to write the following in my Retrofit-interface

@POST("someAddress")
SomeResponse getData();

instead of

@POST("someAddress")
ResponseWrapper<SomeResponse> getData();

There are TypeAdapters for Gson that can do that for definitive types (as in "I have a class Animal and need GSON to deserialize it correctly to Dog, Cat and Orangutan), but NOT for generic types (which is what I would need with my generic payload.

I could register a typeadapter for every possible payload, but that's plain madness, I have over 70 different payload-objects

2 Answers2

2

Have you tried GsonConverterFactory class? I have a more complicated response than yours and it still converts accurately. This converter library is also created by the Retrofit team themselves.

new Retrofit.Builder()
    .baseUrl(url)
    .addConverterFactory(GsonConverterFactory.create())
    .build();
wdina
  • 455
  • 3
  • 11
  • 1
    Deserializing `{ "status":"ok", "payload":{}}` into a `Payload` object is not the issue, the issue is that I want Retrofit or Gson to unwrap it and directly return T to me – TormundThunderfist Jul 14 '17 at 10:55
  • I would have to define all my retrofit calls this way: `@POST() BaseResponse getData();` when I want `@POST() SomeResponse getData();` – TormundThunderfist Jul 14 '17 at 10:56
  • Something like this: @Post() Call> getData(); As long as you define SomeResponse correctly, the library should not have a trouble converting it. – wdina Jul 14 '17 at 11:23
  • If you have any trouble, you can intercept Retrofit logs with HttpLoggingInterceptor, https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor. Hope this helps – wdina Jul 14 '17 at 11:24
  • Payload is exactly what I want to get rid of. It was possible in Retrofit1, so it should be possible for Retrofit2. Thanks for trying to help – TormundThunderfist Jul 14 '17 at 11:27
  • Sorry for misunderstanding your question. If I understand correctly, do you intend to create a Java class during runtime (something like in Javascript or Python)? You could have a look at this thread, https://stackoverflow.com/questions/2320404/creating-classes-dynamically-with-java If you intend to do so, I suggest you convert Payload into a JSONObject object and iterate through each key-value pair and store it in a HashMap (or SparseArray for optimization). Hope this helps – wdina Jul 15 '17 at 02:12
2

Retrofit is doing the serialization part by delegating it to the Converter, you you can add a specific one to the builder by using builder.addConverterFactory(GsonConverterFactory.create()) and there is already many written Retrofit Converters, you can find most of them here.

so if you want to control this process of deserialization, you can write your custom converter, something like this

public class UnwrapConverterFactory extends Converter.Factory {

    private GsonConverterFactory factory;

    public UnwrapConverterFactory(GsonConverterFactory factory) {
        this.factory = factory;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(final Type type,
            Annotation[] annotations, Retrofit retrofit) {
        // e.g. WrappedResponse<Person>
        Type wrappedType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                // -> WrappedResponse<type>
                return new Type[] {type};
            }

            @Override
            public Type getOwnerType() {
                return null;
            }

            @Override
            public Type getRawType() {
                return WrappedResponse.class;
            }
        };
        Converter<ResponseBody, ?> gsonConverter = factory
                .responseBodyConverter(wrappedType, annotations, retrofit);
        return new WrappedResponseBodyConverter(gsonConverter);
    }
}

then you use addConverterFactory() again to tell Retrofit about the new converter. I should mention that you can use multiple converters in Retrofit which is awesome, it simply check converters by order till find the proper one to use.

resources: writing custom Retrofit converter, using multiple converters

Kohei TAMURA
  • 4,970
  • 7
  • 25
  • 49
Mohamed Ibrahim
  • 3,714
  • 2
  • 22
  • 44