BeanDeserializerFactory4920Test.java

package com.fasterxml.jackson.databind.deser.creators;

import java.io.IOException;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;

import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.newJsonMapper;

/**
 * Unit test for [databind#4920]: Creator properties are ignored on abstract types when
 * collecting bean properties, breaking
 * {@link com.fasterxml.jackson.databind.jsontype.impl.AsExternalTypeDeserializer}.
 */
public class BeanDeserializerFactory4920Test
{
    interface TypedData {
        @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", visible = true)
        @JsonTypeIdResolver(ValueTypeIdResolver.class)
        Value getValue();

        String getType();

        @JsonCreator
        static TypedData immutableOf(@JsonProperty("value") Value value, @JsonProperty("type") String type) {
            return new TypedData.Immutable(value, type);
        }

        final class Immutable implements TypedData {

            private final Value value;
            private final String type;

            public Immutable(Value value, String type) {
                this.value = value;
                this.type = type;
            }

            @Override
            public Value getValue() {
                return value;
            }

            @Override
            public String getType() {
                return type;
            }
        }

        final class ValueTypeIdResolver extends TypeIdResolverBase {
            @Override
            public String idFromValue(Object value) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String idFromValueAndType(Object value, Class<?> suggestedType) {
                throw new UnsupportedOperationException();
            }

            @Override
            public JsonTypeInfo.Id getMechanism() {
                return JsonTypeInfo.Id.CUSTOM;
            }

            @Override
            public JavaType typeFromId(DatabindContext context, String id) throws IOException {
                Class<?> type;
                try {
                    type = Class.forName(id);
                } catch (ClassNotFoundException e) {
                    throw new IllegalArgumentException(e);
                }

                return context.constructType(type);
            }
        }
    }

    interface Value {
    }

    static final class StringValue implements Value {

        private final String value;

        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
        public StringValue(String value) {
            this.value = value;
        }

        @JsonValue
        public String getValue() {
            return value;
        }
    }

    static final class LongValue implements Value {

        private final long value;

        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
        public LongValue(long value) {
            this.value = value;
        }

        @JsonValue
        public long getValue() {
            return value;
        }
    }

    @Test
    void testDeserializeAbstract() throws Exception {
        ObjectMapper objectMapper = newJsonMapper();

        //language=JSON
        String json = "{ \"value\": \"1234567890\", \"type\": \"" + StringValue.class.getName() + "\" }";

        TypedData actual = objectMapper.readValue(json, TypedData.class);

        Assertions.assertNotNull(actual);
        Assertions.assertInstanceOf(StringValue.class, actual.getValue());
        Assertions.assertEquals("1234567890", ((StringValue) actual.getValue()).getValue());
    }
}