square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.12k stars 7.3k forks source link

[Bug?] Retrofit Jackson Converter doesn't work with Polymorphism #3643

Open msmerc opened 3 years ago

msmerc commented 3 years ago

I'm trying to get Retrofit to work with polymorphic types, something like this:

    public static interface Vehicle {
    }

    public static class Car implements Vehicle {
        @JsonProperty("type")
        public String type() {  return "car"; }
    }

    public static class Lorry implements Vehicle {
        @JsonProperty("type")
        public String type() {  return "lorry"; }
        @JsonProperty("cargo")
        private String cargo() {  return "toys"; }
    }

Here's my service:

    public interface Service {
        @POST("/api/")
        public Call<String> upload(@Body Vehicle vehicle);
    }

And here's my test:

    @Test
    public void test() throws IOException {
        final OkHttpClient client = new OkHttpClient();
        var retrofit = new Retrofit.Builder()
                .addConverterFactory(JacksonConverterFactory.create())
                .baseUrl("http://whatever/") //We fail before we hit this!
                .client(client)
                .build();

        Service service = retrofit.create(Service.class);
        var call = service.upload(new Car());
        call.execute();

    }

This gives the following error:

java.lang.IllegalArgumentException: Unable to convert SimpleRetrofitTest$Car@14bdbc74 to RequestBody (parameter #1)
    for method Service.upload

    at retrofit2.Utils.methodError(Utils.java:53)
       ...
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class SimpleRetrofitTest$Car and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
        ...
    at retrofit2.converter.jackson.JacksonRequestBodyConverter.convert(JacksonRequestBodyConverter.java:34)
    at retrofit2.converter.jackson.JacksonRequestBodyConverter.convert(JacksonRequestBodyConverter.java:24)
    at retrofit2.ParameterHandler$Body.apply(ParameterHandler.java:411)
    ... 70 more

It's struggling to serialize from the interface to the class. The code works fine if I change the API to: public Call<String> upload(@Body Car vehicle);, so a Car can definitely be serialised. But the JacksonConverter seems to struggle with the polymorphism in this instance.

I've got a complete Gist for this test here: https://gist.github.com/msmerc/4a53b51200bd1e80ce1e9f7ffe3efb16

tomridder commented 1 year ago

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Car.class, name = "car"), @JsonSubTypes.Type(value = Lorry.class, name = "lorry") }) public static interface Vehicle { }