Skip to content

Builder deserailizer: in-compatible type exception when return type is super type #761

@agavrilov76

Description

@agavrilov76

Based on the following stack overflow question: http://stackoverflow.com/questions/29724859/jackson-deserialization-with-builder-pattern-build-method-has-bad-return-type

The builder deserailizer throws the "Build method has bad return type, not compatible with POJO type” exception if the build method return type is a super type of the POJO type. Here is a complete example:

public class JacksonBuilder {
    public interface FooInterface {
    }

    public static class Foo implements FooInterface {
        public static final class Builder {
            @JsonProperty
            public Builder valueA(String value) { return this; }
            @JsonProperty
            public Builder valueB(String value) { return this; }
            public FooInterface build() { return new Foo() {}; }
        }
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Foo.class, name = "foo")})
    public static abstract class FooInterfaceMixin {}

    @JsonTypeName("foo")
    @JsonDeserialize(builder=Foo.Builder.class)
    public static abstract class FooMixin {}

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(FooInterface.class, FooInterfaceMixin.class);
        mapper.addMixIn(Foo.class, FooMixin.class);

        final String jsonString = "{\"type\":\"foo\", \"valueA\":\"a\", \"valueB\":\"b\"}";
        FooInterface foo = mapper.readValue(jsonString, FooInterface.class);
    }
}

Output:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Build method 'stackoverflow.JacksonBuilder$Foo$Builder#build(0 params) has bad return type (stackoverflow.JacksonBuilder$FooInterface), not compatible with POJO type (stackoverflow.JacksonBuilder$Foo)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:270)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:245)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143)
    at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:406)
    at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:176)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:110)
    at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:95)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:131)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3562)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2578)
    at stackoverflow.JacksonBuilder.main(JacksonBuilder.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.IllegalArgumentException: Build method 'stackoverflow.JacksonBuilder$Foo$Builder#build(0 params) has bad return type (stackoverflow.JacksonBuilder$FooInterface), not compatible with POJO type (stackoverflow.JacksonBuilder$Foo)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder.buildBuilderBased(BeanDeserializerBuilder.java:378)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBuilderBasedDeserializer(BeanDeserializerFactory.java:297)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBuilderBasedDeserializer(BeanDeserializerFactory.java:155)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:351)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:265)
    ... 16 more

It seems that the condition at

if (!valueType.getRawClass().isAssignableFrom(rawBuildType)) {
should be reversed to:

!rawBuildType.isAssignableFrom(valueType.getRawClass())

to allow rawBuildType be a super type of the valueType.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions