Skip to content

Should allow deserializing with no-arg @JsonCreator(mode = DELEGATING) (regression) #4688

@carterkozak

Description

@carterkozak

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

2.18.0-rc1 Regression deserializing with no-arg @JsonCreator(mode = DELEGATING)

The refactor to bean property introspection in #4515 caused
existing no-arg delegatring constructors to fail deserialization.

Example from this branch where we test RC releases:
palantir/conjure-java#2349
specifically this test:
https://github.com/palantir/conjure-java/blob/19d7f54eb0001c49b5d8a4bb897b9ad4cb5a28de/conjure-java-core/src/test/java/com/palantir/conjure/java/types/NoFieldBeanTests.java#L36-L39
Using this type:
https://github.com/palantir/conjure-java/blob/19d7f54eb0001c49b5d8a4bb897b9ad4cb5a28de/conjure-java-core/src/integrationInput/java/com/palantir/product/EmptyObjectExample.java

InvalidDefinitionException: Invalid type definition for type `com.palantir.product.EmptyObjectExample`: No argument left as delegating for Creator [method com.palantir.product.EmptyObjectExample#of()]: exactly one required
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]
        at app//com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)
        at app//com.fasterxml.jackson.databind.DeserializationContext.reportBadTypeDefinition(DeserializationContext.java:1866)
        at app//com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addExplicitDelegatingCreator(BasicDeserializerFactory.java:489)
        at app//com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addExplicitDelegatingCreators(BasicDeserializerFactory.java:365)
        at app//com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:270)
        at app//com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:219)
        at app//com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:262)
        at app//com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:151)
        at app//com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:471)
        at app//com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:415)
        at app//com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:317)
        at app//com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:284)
        at app//com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:174)
        at app//com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:669)
        at app//com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:5048)
        at app//com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4918)
        at app//com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3860)
        at app//com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3828)
        at app//com.palantir.conjure.java.types.NoFieldBeanTests.testDeserializeUsesSingleton(NoFieldBeanTests.java:38)

It's not entirely clear that we are correctly using the DELEGATING type here,
perhaps the PROPERTIES type would be a better fit, however neither necessarily
document support for a no-arg constructor. In general we prefer DELEGATING
when possible to avoid ambiguity with potential property sources -- we want
the ability to fail deserialization in this case when any properties are included
in the incoming object.

In 2.17.2, this branch allowed the code to functiona as we desired:

// zero-arg method factory methods fine, as long as explicit
if (argCount == 0) {
creators.setDefaultCreator(factory);
continue;
}

Is there any chance we could allow the previous DELEGATING behavior to continue
to handle the no-arg case? If we'd prefer to move away from DELEGATING for that
case, is there any chance we could emit a deprecation warning for some time?

Version Information

2.18.0-rc1

Reproduction

palantir/conjure-java#2349
specifically this test:
https://github.com/palantir/conjure-java/blob/19d7f54eb0001c49b5d8a4bb897b9ad4cb5a28de/conjure-java-core/src/test/java/com/palantir/conjure/java/types/NoFieldBeanTests.java#L36-L39
Using this type:
https://github.com/palantir/conjure-java/blob/19d7f54eb0001c49b5d8a4bb897b9ad4cb5a28de/conjure-java-core/src/integrationInput/java/com/palantir/product/EmptyObjectExample.java

I will push a PR to jackson-databind which includes a reproducer unit test once I have an issue number from this report :-)
edit: I've created a PR here: #4689

Expected behavior

Ideally no-arg delegating jsoncreators would still be called without throwing an exception.

Additional context

I appreciate that it's possible I'm misusing @JsonCreator(mode = DELEGATING) here, however it's not clear that PROPERTIES is a better option in this case, thought it does appear to work for the time being. Unfortunately the code which uses this pattern is generated using another library I maintain, so it can take months for old api bindings to be replaced, potentially preventing me from upgrading to and beyond 2.18 for much longer than I'm comfortable with!

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.18Issues planned at 2.18 or later

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions