Skip to content

Creator properties are ignored on abstract types when collecting bean properties, breaking AsExternalTypeDeserializer #4920

Closed
@zhenlin-pay2

Description

@zhenlin-pay2

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

When using Jackson with frameworks such as Immutables where users are expected to write bean definitions as abstract types and let the framework generate concrete implementations, Jackson annotations on the abstract type sometimes have no effect, or worse, trigger bugs. This appears to be due to the assumption in BeanDeserializerFactory.addBeanProps that creator properties do not need to be considered for abstract types. In particular, deserialisation fails with a bizarre exception in in the following situation:

  • The type to be deserialised is abstract but with a defined ValueInstantiator (e.g. from a@JsonCreator annotated factory method).
  • The property to be deserialised has a getter but no setter.
  • The type of the property to be deserialised is also abstract.
  • The getter has a @JsonTypeInfo annotation with include = JsonTypeInfo.As.EXTERNAL_PROPERTY.

Version Information

2.17.1

Reproduction

See attached: example.zip

Expected behavior

No response

Additional context

Deserialisation fails with the following exception:

Unexpected token (VALUE_STRING), expected START_ARRAY: need Array value to contain `As.WRAPPER_ARRAY` type information for class example.TypedData$Value
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 2, column: 20] (through reference chain: example.TypedData["value"])
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_STRING), expected START_ARRAY: need Array value to contain `As.WRAPPER_ARRAY` type information for class example.TypedData$Value
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 2, column: 20] (through reference chain: papaya.struct.jackson.TypedData["value"])
	at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
	at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1913)
	at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1699)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:141)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:96)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:61)
	at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:570)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:440)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1493)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4905)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3848)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3816)

Notice that the call chain goes through BeanDeserializer.deserializeFromObjectUsingNonDefault rather than the expected BeanDeserializer.deserializeWithExternalTypeId. This is because _externalTypeIdHandler is null, which is because when BeanDeserializerBase.resolve is called, _beanProperties is empty (!), which is because BeanDeserializerFactory.addBeanProps ignores creator properties when the type is abstract. Changing the value of isConcrete in addBeanProps to true while running in a debugger suffices to fix this problem, though I cannot say if this has any other effects.

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