Skip to content

ParameterNamesModule sets attributes via reflection after calling constructor #72

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

Closed
mabn opened this issue Apr 13, 2018 · 4 comments
Closed
Labels

Comments

@mabn
Copy link

mabn commented Apr 13, 2018

I noticed that parameter names module behaves very weird when using custom naming strategy.
When using SNAKE_CASE naming strategy (probably others too):

  • When constructor parameters are snake-case everything is fine
  • When constructor parameters are camelCase I would expect naming strategy to kick-in and use the constructor in the same way. But the actual behaviour is:
    • constructor is properly called with all the correct attributes

    • but afterwards the attributes are set second time via reflection which effectively skips any code that could potentially alter their values in the constructor.

      The second set happens in PropertyBasedCreator.build because buffer.buffered() is not empty but I don't know jackson internals so I'm not sure if it's ok or not.

I've checked version 2.9.4.
Here's a test case reproducing the problem (and a runnable example with gradle):

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.CoreMatchers.equalTo;

public class ParamNamesTest {
    private static final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
            .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

    public static class Pojo {
        private final String firstAttribute;

        private final Long secondAttribute;

        public Pojo(String firstAttribute,
                    Long secondAttribute) {
            this.firstAttribute = "not-from-jackson";
            this.secondAttribute = secondAttribute;
        }

        public String getFirstAttribute() {
            return firstAttribute;
        }

        public Long getSecondAttribute() {
            return secondAttribute;
        }

        @Override
        public String toString() {
            return "Collaboration{" +
                    "firstAttribute='" + firstAttribute + '\'' +
                    ", secondAttribute=" + secondAttribute +
                    '}';
        }
    }

    @Test
    public void jacksonUsesConstructorOnly() throws IOException {
        String str = "{\"first_attribute\":\"alice\", \"second_attribute\":1267518}";
        Pojo pojo = mapper.readValue(str, Pojo.class);

        System.out.println(pojo);
        Assert.assertThat(pojo.getFirstAttribute(), equalTo("not-from-jackson"));
    }
}
@cowtowncoder
Copy link
Member

First of all: thank you for reporting this, and especially including version affected.
I hope to verify this soon with 2.9.5, and assuming problem still remains (which seems likely), see what is going on. Usage looks fine. About the only thing you could try first is to add @JsonCreator to constructor, to ensure it is recognized (detection may fail for a few reasons, such as compiler not adding actual parameter name information in bytecode).

@mabn
Copy link
Author

mabn commented Apr 17, 2018

Thanks for looking at this.

Your intuition is correct, adding @JsonCreator helps and the test passes - so it's only a problem when the constructor is not annotated.

@cowtowncoder
Copy link
Member

I think this is actually same as:

FasterXML/jackson-databind#806

and the unfortunate part is that it is rather difficult to fix, at least for 2.x.
So there is a chance that this might only get fixed for 3.0 where I hope to fully rewrite creator detection logic, basically so that property detection starts by considering Creators, which should allow keeping accessors (fields, getters, setters) and creator parameters in sync.

What happens here, essentially, is that implicit detection of constructors occurs too late for naming strategy to be applied, which leads to name discrepancy.

@cowtowncoder cowtowncoder added 3.0 and removed 2.9 labels Aug 13, 2019
@cowtowncoder
Copy link
Member

I don't think this is specific to parameter names module, and since there is matching issue for jackson-databind (see previous comment), closing this one as duplicate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants