Describe the bug
Deserialization of a specific class becomes broken if another specific class is deserialized before it. All classes are immutable and depend on @JsonCreator and property-based constructors with all properties annotated correctly with @JsonProperty.
If a class ContainerOne is deserialized before a class ContainerTwo (see below for reference), deserialization will fail, but it you flip their order, it will work as expected.
It appears that some sort of caching is handled incorrectly making the second invocation ignore the property-based creator.
Version information
Reproduced on 2.12.3 and 2.13.0
To Reproduce
Here is the minimal setup I've managed to make to reproduce this.
public class Common {
private final String property;
private final List<ContainerTwo> twos;
@JsonCreator
public Common(@JsonProperty("property") final String property,
@JsonProperty("twos") final List<ContainerTwo> twos) {
this.property = property;
this.twos = twos;
}
public String getProperty() {
return property;
}
// @JsonIgnoreProperties("common") // This is commented out because it does not contribute to the reproduction, but would be present for such a setup.
public List<ContainerTwo> getTwos() {
return twos;
}
}
public class ContainerOne {
private final Common common;
@JsonCreator
public ContainerOne(@JsonProperty("common") final Common common) {
this.common = common;
}
public Common getCommon() {
return common;
}
}
public class ContainerTwo {
private final Common common;
@JsonCreator
public ContainerTwo(@JsonProperty("common") final Common common) {
this.common = common;
}
@JsonIgnoreProperties("twos")
public Common getCommon() {
return common;
}
}
public static void main(final String[] args) throws Exception {
final String oneJson = "{ \"common\": { \"property\": \"valueOne\" } }";
final String twoJson = "{ \"common\": { \"property\": \"valueTwo\" } }";
final ObjectMapper objectMapper = new ObjectMapper();
final ContainerOne one = objectMapper.readValue(oneJson, ContainerOne.class);
final ContainerTwo two = objectMapper.readValue(twoJson, ContainerTwo.class); // This fails, unless invoked before the line directly above.
}
I have found two ways to stop this from happening, both of which are not really an option for me at this point:
- Remove
@JsonIgnoreProperties("twos") from the getter in ContainerTwo
- Make
Common mutable, with setters
Expected behavior
Since this behavior is specific to the execution order and breaks seemingly unexpectedly, it appears to be a bug.
Expected behavior should be that both objects are deserialized (like they are individually) regardless of their invocation order.
Additional Information
Here is the full stack trace of the exception that happens
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.dinopraso.serialization.json.Common` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{ "common": { "property": "valueTwo" } }"; line: 1, column: 15] (through reference chain: com.dinopraso.serialization.json.ContainerTwo["common"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1728)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1353)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:563)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:438)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3598)
Describe the bug
Deserialization of a specific class becomes broken if another specific class is deserialized before it. All classes are immutable and depend on
@JsonCreatorand property-based constructors with all properties annotated correctly with@JsonProperty.If a class
ContainerOneis deserialized before a classContainerTwo(see below for reference), deserialization will fail, but it you flip their order, it will work as expected.It appears that some sort of caching is handled incorrectly making the second invocation ignore the property-based creator.
Version information
Reproduced on 2.12.3 and 2.13.0
To Reproduce
Here is the minimal setup I've managed to make to reproduce this.
I have found two ways to stop this from happening, both of which are not really an option for me at this point:
@JsonIgnoreProperties("twos")from the getter inContainerTwoCommonmutable, with settersExpected behavior
Since this behavior is specific to the execution order and breaks seemingly unexpectedly, it appears to be a bug.
Expected behavior should be that both objects are deserialized (like they are individually) regardless of their invocation order.
Additional Information
Here is the full stack trace of the exception that happens