Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonCreator on constructor not compatible with @JsonIdentityInfo, PropertyGenerator #2944

Closed
symposion opened this issue Nov 18, 2020 · 6 comments
Labels
has-failing-test Indicates that there exists a test case (under `failing/`) to reproduce the issue
Milestone

Comments

@symposion
Copy link
Contributor

symposion commented Nov 18, 2020

On Jackson 2.11.3

A bean annotated with @JsonIdentityInfo pointing at an id property will not deserialize correctly using @JsonCreator on its constructor. The code explicitly ignores identity fields when looking for constructor parameters (they are set after creation) but this makes JsonIdentityInfo incompatible with any sort of immutable object that requries all its properties to be set at construction time.

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new ParameterNamesModule());
        mapper.readValue(JSON, JsonBean.class);
    }


    private static final String JSON = "{\"id\": \"myId\",\"value\": \"myValue\"}";

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    public static class JsonBean {

        private final String id;
        private final String value;

        @JsonCreator
        public JsonBean(String id, String value) {
            System.out.println(id);
            this.id = id;
            this.value = value;
        }

        public String getId() {
            return id;
        }

        public String getValue() {
            return value;
        }
    }

}

I would expect this to print out "myId"; instead it prints "null". Note that you need to compile with -parameters for this to work

@symposion symposion added the to-evaluate Issue that has been received but not yet evaluated label Nov 18, 2020
@cowtowncoder
Copy link
Member

Thank you for reporting this: I hope to have a look at it soon.

@cowtowncoder
Copy link
Member

I can reproduce this; added failing test. Looks like CreatorProperty is somehow not linked to identity id -- so in the example case, field id is used as accessor (even if it is final, actually, unless specifically configured not to do that).

@cowtowncoder cowtowncoder added has-failing-test Indicates that there exists a test case (under `failing/`) to reproduce the issue and removed to-evaluate Issue that has been received but not yet evaluated labels Nov 25, 2020
@symposion
Copy link
Contributor Author

I spent some time looking into this. In BeanDeserializer._deserializeUsingPropertyBased (and probably various other related methods on other classes) it specifically excludes id properties from being added to the PropertyBuffer:

           // Object Id property?
            if (buffer.readIdProperty(propName)) {
                continue;
            }

This looks very deliberate but I don't understand why it's necessary. The intention seems to be that id properties are handled at the end when the object is finally built. PropertyBasedCreator.build() calls through to buffer.handeIdValue which will attempt to set the id value using a setter if one is available as well as registering the object with the id resolver. Given that PropertyBasedCreator (AIUI) is only used for those cases where we're explicitly not using setters but instead passing the value to the creator method/constructor this seems like a mistake. Perhaps a copy/paste from some other creation code? If I get a chance I'll try just removing that continue and see if it breaks anything

symposion added a commit to symposion/jackson-databind that referenced this issue Nov 26, 2020
@cowtowncoder
Copy link
Member

@symposion Couple of notes.

First, use of PropertyBasedCreator does not exclude possibility of also using setters/fields (although some users prefer it would; there's an issue to allow forcing that), or "any setter". So the idea is that anything not-yet handled would be buffered, and anything that is handled is skipped.

Second, I think (but am not 100% sure) that skipping of Object Id is due to most Object Ids being considered (identity) metadata, that is, something for which there is no matching property. The exception to this is the use of PropertyGenerator which relies on existing property.

I think you are probably in thinking that skipping should not occur automatically.

Somehow, the id CreatorProperty is not linked in a way it should be -- it may even be that it is removed intentionally (but for this case, incorrectly).

@cowtowncoder
Copy link
Member

Ah. Missed the note on PR -- thank you, hope to check this either today or tomorrow!

@symposion
Copy link
Contributor Author

Yeah, it seems to work for my use case and none of the tests break... but Jackson is complicated and covers a lot of use cases so I'm not sure if there's some corner case that my fix would cause problems for.

cowtowncoder pushed a commit that referenced this issue Nov 27, 2020
@cowtowncoder cowtowncoder changed the title JsonCreator on constructor not compatible with JsonIdentityInfo @JsonCreator on constructor not compatible with @JsonIdentityInfo, PropertyGenerator Nov 27, 2020
cowtowncoder added a commit that referenced this issue Nov 27, 2020
@cowtowncoder cowtowncoder added this to the 2.12.0 milestone Nov 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has-failing-test Indicates that there exists a test case (under `failing/`) to reproduce the issue
Projects
None yet
Development

No branches or pull requests

2 participants