XciD
XciD

Reputation: 2605

Default Class Polymorphism Jackson Java

I'm using the @JsonTypeInfo annotation of jackson in order to define a way to deserialize a property that is defined by an Interface.

I'm using include = JsonTypeInfo.As.EXTERNAL_PROPERTY property in order to set the property included in the outer object. I also using defaultImpl to set the failsafe class if the property is not set.

I would like that if the id is missing, the failback class is used to deserialize the parameters property like in the unit test:

public class DeserializationTest {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public static class OuterClass {
        public String id;

        @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            defaultImpl = InnerImpl.class,
            include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
            property = "id"
        )
        @JsonSubTypes(
            @JsonSubTypes.Type(value = InnerImpl2.class, name = "non_default")
        )
        public Parameters parameters;
    }

    public interface Parameters {
    }

    public static class InnerImpl implements Parameters {
        public String property;
    }

    public static class InnerImpl2 implements Parameters {
        public String property2;
    }

    @Test
    public void testDeserializationDefault() throws IOException {
        OuterClass o = OBJECT_MAPPER.readValue(
            "{\"parameters\": {\"property\":\"test\"}, \"id\":\"default\"}", OuterClass.class
        );

        Assert.assertEquals(InnerImpl.class, o.parameters.getClass());
        Assert.assertEquals("test", ((InnerImpl) o.parameters).property);
    }

    @Test
    public void testDeserializationDefaultWithoutId() throws IOException {
        OuterClass o = OBJECT_MAPPER.readValue(
            "{\"parameters\": {\"property\":\"test\"}}", OuterClass.class
        );

        Assert.assertEquals(InnerImpl.class, o.parameters.getClass());
        Assert.assertEquals("test", ((InnerImpl) o.parameters).property);
    }

    @Test
    public void testDeserializationNonDefault() throws IOException {
        OuterClass o = OBJECT_MAPPER.readValue(
            "{\"parameters\": {\"property2\":\"test\"}, \"id\":\"non_default\"}", OuterClass.class
        );
        Assert.assertEquals(InnerImpl2.class, o.parameters.getClass());
        Assert.assertEquals("test", ((InnerImpl2) o.parameters).property2);
    }
}

But I got this error on the second test: testDeserializationDefaultWithoutId

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.ovh.serving.hub.SerializationTest$InnerImpl` out of VALUE_NULL token
 at [Source: (String)"{"parameters": {"storagePath":"storagePathTest"}}"; line: 1, column: 49]

EDIT: Add use case with specific id

EDIT2: It's works with JsonTypeInfo.As.PROPERTY if the id is in the inner class

Upvotes: 4

Views: 1142

Answers (1)

ahoxha
ahoxha

Reputation: 1938

You are telling Jackson to include an external property named id, that's why it requires the id property.

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
    property = "id",
    defaultImpl = InnerImpl.class
)

Remove include and property from @JsonTypeInfo and it will work. Your annotation should be:

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    defaultImpl = InnerImpl.class
)

Upvotes: 1

Related Questions