DefaultTypeResolver3505Test.java

package com.fasterxml.jackson.databind.jsontype.deftyping;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

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

// Tests for [databind#3505] causing a NPE when setting a DefaultTypeResolverBuilder
// and registering subtypes through ObjectMapper (no annotations)
public class DefaultTypeResolver3505Test
    extends DatabindTestUtil
{
    interface Parent {
        class ChildOne implements Parent {
            public String one;
        }

        class ChildTwo implements Parent {
            public String two;
        }
    }

    // This class is technically not needed for the test to fail without the fix
    // (AsDeductionTypeDeserializer will crash in #buildFingerprints), but was
    // added to have more assertions on the subtypes values
    @SuppressWarnings("serial")
    static final class AssertingTypeResolverBuilder
        extends ObjectMapper.DefaultTypeResolverBuilder
    {
        public AssertingTypeResolverBuilder() {
            super(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS,
                    BasicPolymorphicTypeValidator.builder().allowIfSubType(Parent.class).build());
        }

        @Override
        public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
            // this also gets called for String, not sure if that's expected with PTV
            // this behaviour is outside the scope of this test
            if (baseType.isAbstract()) {
                assertNotNull(subtypes);
                assertEquals(2, subtypes.size());
                final List<NamedType> expected = new ArrayList<>(2);
                expected.add(new NamedType(Parent.ChildOne.class));
                expected.add(new NamedType(Parent.ChildTwo.class));
                assertTrue(subtypes.containsAll(expected));
            }

            return super.buildTypeDeserializer(config, baseType, subtypes);
        }
    }

    @Test
    public void testDeductionWithDefaultTypeResolverBuilder() throws Exception {
        final ObjectMapper mapper = jsonMapperBuilder()
                .registerSubtypes(Parent.ChildOne.class, Parent.ChildTwo.class)
                .setDefaultTyping(new AssertingTypeResolverBuilder()
                        .init(JsonTypeInfo.Id.DEDUCTION, null))
                .build();

        final Parent firstRead = mapper.readValue("{ \"one\": \"Hello World\" }", Parent.class);
        assertNotNull(firstRead);
        assertTrue(firstRead instanceof Parent.ChildOne);
        assertEquals("Hello World", ((Parent.ChildOne) firstRead).one);
        final Parent secondRead = mapper.readValue("{ \"two\": \"Hello World\" }", Parent.class);
        assertNotNull(secondRead);
        assertTrue(secondRead instanceof Parent.ChildTwo);
        assertEquals("Hello World", ((Parent.ChildTwo) secondRead).two);
    }
}