3

I am really confused how jackson (2.9.6 version) ObjectMapper works with @ConstructorProperties annotation.

It seems that mapper ignores property names which are present in a @ConstructorPropertiesannotation value method.

What's even more interesting - mapper works correctly regardless of properties names.

What I am talking about?

Let's consider custom XmlMapper:

private static final ObjectMapper XML_MAPPER = new XmlMapper()
            .setAnnotationIntrospector(
                    AnnotationIntrospector.pair(
                            new JaxbAnnotationIntrospector(),
                            new JacksonAnnotationIntrospector()
                    )
            )
            .registerModule(new JavaTimeModule())
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
            .setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);

and simple Data Transfer Object (DTO):

    @XmlRootElement(name = "person")
    @XmlAccessorType(XmlAccessType.NONE)
    static class Person {
        @XmlAttribute
        final int age;

        @XmlAttribute
        final String name;

        @XmlAttribute
        final LocalDate dateOfBirth;

        @ConstructorProperties({"age","name","dateOfBirth"})
        public Person(int age, String name, LocalDate dateOfBirth) {
            this.age = age;
            this.name = name;
            this.dateOfBirth = dateOfBirth;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", dateOfBirth=" + dateOfBirth +
                    '}';
        }
    }

I created test to reproduce the issue:

@Test
@DisplayName("Check xml deseralization for Person class")
void deserialization() throws IOException {
    String xml = "<person age=\"26\" name=\"Fred\" date-of-birth=\"1991-11-07\"/>";
    Person person = XML_MAPPER.readValue(xml, Person.class);
    Assertions.assertEquals("Person{age=26, name='Fred', dateOfBirth=1991-11-07}", person.toString());
}

It's strange for me why test is passed regardless of @ConstructorProperties annotation. The test passed with annotation

        @ConstructorProperties({"a","b","c"})
        public Person(int age, String name, LocalDate dateOfBirth) {
            this.age = age;
            this.name = name;
            this.dateOfBirth = dateOfBirth;
        }

Is it a magic? How jackson processes this annotation? What is an equivalent in jackson annotations to ConstructorProperties?

4

1 回答 1

1

它通过了,因为可以从注释JaxbAnnotationIntrospector中确定属性名称。@XmlAttribute

上的文档AnnotationIntrospectorPair说:

允许使用 2 个自省器的帮助器类,以便一个自省器充当主要使用的自省器;如果主要方法没有为方法提供决定性或有用的结果,则使用第二个作为后备。

JacksonAnnotationIntrospector它理解@ConstructorProperties注释)根本没有被使用。

如果删除所有 JAXB 注释,则只有在@ConstructorProperties.

如果您想以“杰克逊方式”执行此操作,则删除 JAXB 注释并JaxbAnnotationIntrospector完全删除(只需放弃对 的调用setAnnotationIntrospector,映射器将默认使用JacksonAnnotationIntrospector)。

反序列化将起作用,但如果您想获得相同的序列化形式,则必须添加一些杰克逊本机注释:

@JacksonXmlRootElement(localName = "person")
static class Person {
    @JacksonXmlProperty(isAttribute = true)
    final int age;

    @JacksonXmlProperty(isAttribute = true)
    final String name;

    @JacksonXmlProperty(isAttribute = true)
    final LocalDate dateOfBirth;

    @ConstructorProperties({"age", "name", "dateOfBirth"})
    public Person(int age, String name, LocalDate dateOfBirth) {
        this.age = age;
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    //...
于 2018-09-19T08:06:16.147 回答