JsonTypeInfoCustomResolver2811Test.java

package com.fasterxml.jackson.databind.jsontype;

import java.io.IOException;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.*;

public class JsonTypeInfoCustomResolver2811Test extends DatabindTestUtil
{
    interface Vehicle { }

    static class Car implements Vehicle {
        public int wheels;
        public String color;
    }

    static class Bicycle implements Vehicle {
        public int wheels;
        public String bicycleType;
    }

    static class Person<T extends Vehicle> {
        public String name;
        public VehicleType vehicleType;

        @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
                property = "vehicleType", defaultImpl = Car.class
        )
        @JsonTypeIdResolver(VehicleTypeResolver.class)
        public T vehicle;
    }

    public enum VehicleType {
        CAR(Car.class),
        BICYCLE(Bicycle.class);

        public final Class<? extends Vehicle> vehicleClass;

        VehicleType(Class<? extends Vehicle> vehicleClass) {
            this.vehicleClass = vehicleClass;
        }
    }

    static class VehicleTypeResolver extends TypeIdResolverBase {
        JavaType superType;

        @Override
        public void init(JavaType bt) {
            this.superType = bt;
        }

        @Override
        public String idFromValue(Object value) {
            return idFromValueAndType(value, value.getClass());
        }

        @Override
        public String idFromValueAndType(Object value, Class<?> suggestedType) {
            // only to be called for default type but...
            return suggestedType.getSimpleName().toUpperCase();
        }

        @Override
        public JavaType typeFromId(DatabindContext context, String id) throws IOException {
            Class<? extends Vehicle> vehicleClass;
            try {
                vehicleClass = VehicleType.valueOf(id).vehicleClass;
            } catch (IllegalArgumentException e) {
                throw new IOException(e.getMessage(), e);
            }
            return context.constructSpecializedType(superType, vehicleClass);
        }

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

    private final ObjectMapper MAPPER = jsonMapperBuilder()
            .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY)
            .disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
            .build();

    // [databind#2811]
    @Test
    public void testTypeInfoWithCustomResolver2811NoTypeId() throws Exception
    {
        String json = "{ \"name\": \"kamil\", \"vehicle\": {\"wheels\": 4, \"color\": \"red\"}}";
        Person<?> person = MAPPER.readValue(json, Person.class);
        assertEquals("kamil", person.name);
        assertNull(person.vehicleType);
        assertNotNull(person.vehicle);
        assertEquals(Car.class, person.vehicle.getClass());
    }

    // Passing case for comparison
    /*
    @Test
    public void testTypeInfoWithCustomResolver2811WithTypeId() throws Exception
    {
        String json = "{\n" +
                "  \"name\": \"kamil\",\n" +
                "  \"vehicleType\": \"CAR\",\n" +
                "  \"vehicle\": {\n" +
                "    \"wheels\": 4,\n" +
                "    \"color\": \"red\"\n" +
                "  }\n" +
                "}"
                ;
        Person<?> person = MAPPER.readValue(json, Person.class);
        assertEquals("kamil", person.name);
        assertEquals(VehicleType.CAR, person.vehicleType);
        assertNotNull(person.vehicle);
    }
    */
}