Jackson ConstructorProperties игнорирует имена свойств - PullRequest
0 голосов
/ 18 сентября 2018

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

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

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

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

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

1 Ответ

0 голосов
/ 19 сентября 2018

Это проходит, потому что 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;
    }

    //...
...