question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Deserialization fails depending on the order of deserialized objects with "Cannot construct instance (although at least one Creator exists)"

See original GitHub issue

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:

  1. Remove @JsonIgnoreProperties("twos") from the getter in ContainerTwo
  2. 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)

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
dinoprasocommented, Jan 20, 2022

Though the issue still remains to be fixed, since the fact that a set of properties was ignored on one class shouldn’t affect the deserialization of another

0reactions
cowtowncodercommented, Jan 21, 2022

The behavior about “unifying annotations” (combining) – that annotations in one accessor are indeed applied to the “whole” logical property, regardless of which accessor had it – is indeed 100% intended and working as defined. The reason for this is to reduce the need to apply multiple duplicate annotations for things like renaming (as illustrated by @yawkat’s example). The process of unification is applied for all Jackson annotations without explicit rules in trying to determine separate semantics for separate annotations. But even if handling were separated, I would argue that @JsonIgnoreProperties should apply regardless of setter/getter used to denote it.

Having said that it sounds like there is a separate issue which is likely due to the way that @JsonIgnoreProperties is applied via reference. It sounds likely that the “modified” instance (with ignoral) gets cached and incorrectly used for non-annotated case. I think there is another existing issue filed for recursive use case that is probably related.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deserialization error : Cannot construct instance of Dto ...
In classes with only one attribute, to deserialize an object Json need a nos argument constructor from that class. In your case you...
Read more >
Jackson: java.util.LinkedHashMap cannot be cast to X
ClassCastException: java.util.LinkedHashMap cannot be cast to X when we try to deserialize JSON or XML into a collection of objects.
Read more >
IICS task updating lead object using Marketo V3 connector ...
Cannot construct instance of `com.marketo.mapi.webapp.entities.json.SyncLeadRequest` (although at least one Creator exists): no ...
Read more >
Definitive Guide to Jackson ObjectMapper - Serialize and ...
The FAIL_ON_NULL_FOR_PRIMITIVES feature determines whether to fail when encountering JSON properties as null while deserializing into Java ...
Read more >
Serialization with Jackson - Documentation - Akka
Jackson has support for both text based JSON and binary formats. ... whenever we need to deserialize an instance of Unicorn trait, we...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found