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?
It's passing because the
JaxbAnnotationIntrospectorcan determine the property names from the@XmlAttributeannotations.The doc on
AnnotationIntrospectorPairsays:The
JacksonAnnotationIntrospector(which understands the@ConstructorPropertiesannotation) isn't being used at all.If you remove all the JAXB annotations your test will only pass when the correct names are specified in
@ConstructorProperties.If you want to do it "the jackson way", then remove the JAXB annotations and the
JaxbAnnotationIntrospectorcompletely (just drop the call tosetAnnotationIntrospector, the mapper will default to using theJacksonAnnotationIntrospector).Deserialisation will work, but you'll have to add some jackson native annotations if you want to achieve the same serialised form: