EDITAR : Voici, dans un article de blog par les mainteneurs de Jackson, il semble que la 2.12 pourrait voir des améliorations en ce qui concerne l'injection de constructeur. (La version actuelle au moment de cette édition est 2.11.1)
Amélioration de l'autodétection des créateurs de constructeurs, y compris la résolution et l'atténuation des problèmes liés aux constructeurs ambigus à un argument (délégation et propriétés).
Cela reste vrai pour Jackson databind 2.7.0.
Le Jackson @JsonCreator
annotation 2.5 javadoc o Documentation sur les annotations Jackson grammaire ( Constructeur s et méthode d'usine s ) laissez croire en effet que l'on peut marque plusieurs constructeurs.
Annotation de marqueur qui peut être utilisée pour définir les constructeurs et les méthodes de fabrique comme étant ceux à utiliser pour instancier de nouvelles instances de la classe associée.
En regardant le code où le créateurs sont identifiés, il semble que le Jackson CreatorCollector
ignore Constructeurs surchargés parce que c'est seulement vérifie le premier argument du constructeur .
Class<?> oldType = oldOne.getRawParameterType(0);
Class<?> newType = newOne.getRawParameterType(0);
if (oldType == newType) {
throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
+" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
}
-
oldOne
est le premier créateur de constructeur identifié.
-
newOne
est le créateur du constructeur surchargé.
Cela signifie que le code comme celui-ci ne fonctionne pas
@JsonCreator
public Phone(@JsonProperty("value") String value) {
this.value = value;
this.country = "";
}
@JsonCreator
public Phone(@JsonProperty("country") String country, @JsonProperty("value") String value) {
this.value = value;
this.country = country;
}
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336"); // raise error here
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
Mais ce code fonctionnera :
@JsonCreator
public Phone(@JsonProperty("value") String value) {
this.value = value;
enabled = true;
}
@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
this.value = value;
this.enabled = enabled;
}
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");
C'est un peu bricolé et peut ne pas être à l'épreuve du temps. .
La documentation est vague sur la façon dont la création d'objets fonctionne ; d'après ce que j'ai compris du code, il est possible de mélanger différentes méthodes :
Par exemple, on peut avoir une méthode d'usine statique annotée avec @JsonCreator
@JsonCreator
public Phone(@JsonProperty("value") String value) {
this.value = value;
enabled = true;
}
@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
this.value = value;
this.enabled = enabled;
}
@JsonCreator
public static Phone toPhone(String value) {
return new Phone(value);
}
assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");
Cela fonctionne mais ce n'est pas idéal. En fin de compte, cela pourrait avoir du sens, par exemple si le JSON est le suivant dynamique alors il faudrait peut-être envisager d'utiliser un constructeur délégué pour gérer les variations de charge utile de manière beaucoup plus élégante qu'avec de multiples constructeurs annotés.
Notez également que Jackson ordres créateurs par priorité par exemple dans ce code :
// Simple
@JsonCreator
public Phone(@JsonProperty("value") String value) {
this.value = value;
}
// more
@JsonCreator
public Phone(Map<String, Object> properties) {
value = (String) properties.get("value");
// more logic
}
assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");
Cette fois-ci, Jackson ne soulèvera pas d'erreur, mais il n'utilisera que l'option délégué Constructeur Phone(Map<String, Object> properties)
ce qui signifie que le Phone(@JsonProperty("value") String value)
n'est jamais utilisé.