Skip to content

ParameterNamesModule sets attributes via reflection after calling constructor #72

Closed
@mabn

Description

@mabn

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"));
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions