Jackson ConstructorProperties игнорирует имена свойств

Я действительно запутался, как Джексон (версия 2.9.6) ObjectMapper работает с аннотацией @ConstructorProperties.

Кажется, что картограф игнорирует имена свойств, которые присутствуют в методе значения @ConstructorPropertiesannotation.

Что еще интереснее - маппер работает корректно вне зависимости от названий свойств.

О чем я говорю?

Рассмотрим пользовательский 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);

и простой объект передачи данных (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 +
                    '}';
        }
    }

Я создал тест, чтобы воспроизвести проблему:

@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());
}

Мне странно, почему тест проходит независимо от аннотации @ConstructorProperties. Тест пройден с аннотацией

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

Это магия? Как Джексон обрабатывает эту аннотацию? Что эквивалентно аннотациям Джексона для ConstructorProperties?


person yanefedor    schedule 18.09.2018    source источник


Ответы (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;
    }

    //...
person teppic    schedule 19.09.2018