If there is a no-parameter constructor marked as JsonCreator and a constructor reported as DefaultCreator, latter is incorrectly used. (original) (raw)

Search before asking

Describe the bug

If both JsonCreator and DefaultCreator are reported, the JsonCreator should take precedence.
On the other hand, in certain cases, the DefaultCreator may take precedence.

This issue is a Javaized version of the following
FasterXML/jackson-module-kotlin#932
#5040

Version Information

Reproduction

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; import com.fasterxml.jackson.databind.introspect.PotentialCreator; import com.fasterxml.jackson.databind.json.JsonMapper; import org.junit.jupiter.api.Test;

import java.util.List;

public class Kotlin932Java { static class User { private final int age;

    public User(int age) { this.age = age; }

    @JsonCreator
    public User() { this(0); }

    public int getAge() { return age; }
}

static class AI extends NopAnnotationIntrospector {
    @Override
    public PotentialCreator findDefaultCreator(MapperConfig<?> config,
                                               AnnotatedClass valueClass,
                                               List<PotentialCreator> declaredConstructors,
                                               List<PotentialCreator> declaredFactories) {
        if (valueClass.getRawType() != User.class) return null;

        return declaredConstructors.stream()
                .filter(it -> it.paramCount() != 0)
                .findFirst()
                .orElse(null);
    }
}

@Test
public void kotlin932() throws JsonProcessingException {
    ObjectMapper mapper = JsonMapper.builder().annotationIntrospector(new AI()).build();
    String json =
            "{\n" +
            "  \"age\": 25\n" +
            "}";

    User user = mapper.readValue(json, User.class);

    System.out.println(user);
}

}

Expected behavior

Deserialization should succeed, but in fact has no property name ... is reported.

Additional context

The full stack trace when run on the 2.19 branch of kotlin-module.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `com.fasterxml.jackson.module.kotlin.test.github.Kotlin932Java$User`: Argument #0 of Creator [constructor for `com.fasterxml.jackson.module.kotlin.test.github.Kotlin932Java$User` (1 arg), annotations: [null] has no property name (and is not Injectable): can not use as property-based Creator
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]

    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadTypeDefinition(DeserializationContext.java:1893)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addSelectedPropertiesBasedCreator(BasicDeserializerFactory.java:535)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:259)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:209)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:263)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:152)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:472)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:416)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:318)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:285)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:175)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:669)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:5102)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4972)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3887)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3855)
    at com.fasterxml.jackson.module.kotlin.test.github.Kotlin932Java.kotlin932(Kotlin932Java.java:50)
    at java.base/java.lang.reflect.Method.invoke(Method.java:569)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)