Skip to content

Converter annotation not honored for abstract types #795

@myrosia

Description

@myrosia

I have a use case (described also in this Stackoverflow question: http://stackoverflow.com/questions/30264115/how-to-use-jackson-deserializer-converter-correctly) where I would like to implement custom parsing for string values into types based on a legacy parser. I implemented a converter extending StdConverter that takes a string and runs my legacy parser to parse it in my desired type. I then tried to use @JsonDeserialize with converter argument.

This works when my field is a concrete class. But if my field is declared to be of an abstract type, even if the converter correctly returns a concrete instance, I get an exception.

More specifically, in the test case below, I would expect both testNonAbstractTypeDeserialization and testAbstractTypeDeserialization to pass, because they use very similar converters. But in practice (Jackson 2.5.0) testNonAbstractTypeDeserialization passes, and testAbstractTypeDeserialization fails with the following exception:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of JacksonTest$AbstractCustomType, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: {"customField": "customString"}; line: 1, column: 2]

The JUnit test case is below

import java.io.IOException;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.Converter;
import com.fasterxml.jackson.databind.util.StdConverter;

import static org.junit.Assert.*;

public class JacksonTest {
    public static abstract class AbstractCustomType {
        private final String value;
        public AbstractCustomType(String v) {
            this.value = v;
        }
    }

    public static class ConcreteCustomType extends AbstractCustomType {
        public ConcreteCustomType(String v) {
            super(v);
        }
    }

    public static class AbstractCustomTypeDeserializationConverter extends StdConverter<String, AbstractCustomType>{

        @Override
        public AbstractCustomType convert(String arg) {
            return new ConcreteCustomType(arg);
        }
    }


    public static class AbstractCustomTypeUser {
        @JsonProperty
        @JsonDeserialize(converter = AbstractCustomTypeDeserializationConverter.class)
        private final AbstractCustomType customField;

        @JsonCreator AbstractCustomTypeUser(@JsonProperty("customField") AbstractCustomType customField) {
            this.customField = customField;
        }
    }


    public static  class NonAbstractCustomType {
        private final String value;
        public NonAbstractCustomType(String v) {
            this.value = v;
        }
    }


    public static class NonAbstractCustomTypeDeserializationConverter extends StdConverter<String, NonAbstractCustomType>{

        @Override
        public NonAbstractCustomType convert(String arg) {
            return new NonAbstractCustomType(arg);
        }
    }


    public static class NonAbstractCustomTypeUser {
        @JsonProperty
        @JsonDeserialize(converter = NonAbstractCustomTypeDeserializationConverter.class)
        private final NonAbstractCustomType customField;

        @JsonCreator NonAbstractCustomTypeUser(@JsonProperty("customField") NonAbstractCustomType customField) {
            this.customField = customField;
        }
    }

    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    @Test
    public void testAbstractTypeDeserialization() throws IOException {
        String test="{\"customField\": \"customString\"}";
        AbstractCustomTypeUser cu = JSON_MAPPER.readValue(test, AbstractCustomTypeUser.class);
        assertNotNull(cu);
    }

    @Test
    public void testNonAbstractDeserialization() throws IOException {
        String test="{\"customField\": \"customString\"}";
        NonAbstractCustomTypeUser cu = JSON_MAPPER.readValue(test, NonAbstractCustomTypeUser.class);
        assertNotNull(cu);
    }

}

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