EDITAR: He aquí, en una publicación de blog de los mantenedores de Jackson, parece que 2.12 puede ver mejoras con respecto a la inyección del constructor. (La versión actual en el momento de esta edición es 2.11.1)
Mejore la detección automática de los creadores de constructores, incluida la resolución / alivio de problemas con constructores ambiguos de 1 argumento (delegar frente a propiedades)
Esto sigue siendo válido para Jackson databind 2.7.0.
The Jackson @JsonCreator
anotación 2.5 javadoc o Jackson anotaciones documentación de la gramática ( constructor s y método de fábrica s ) dejar que creen en verdad que uno puede marcar varios constructores.
Anotación de marcador que se puede usar para definir constructores y métodos de fábrica como uno para usar para crear instancias nuevas de la clase asociada.
Al observar el código donde se identifican los creadores , parece que Jackson CreatorCollector
está ignorando los constructores sobrecargados porque solo verifica el primer argumento del constructor .
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
es el primer creador de constructores identificado.
newOne
es el creador del constructor sobrecargado.
Eso significa que un código como ese no funcionará
@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");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
Pero este código funcionará:
@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");
Esto es un poco engañoso y puede que no sea a prueba de futuro .
La documentación es vaga sobre cómo funciona la creación de objetos; Sin embargo, de lo que obtengo del código, es que es posible mezclar diferentes métodos:
Por ejemplo, se puede tener un método de fábrica estático anotado con @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");
Funciona pero no es ideal. Al final, podría tener sentido, por ejemplo, si el json es así de dinámico, entonces quizás uno debería buscar usar un constructor delegado para manejar las variaciones de carga útil de manera mucho más elegante que con múltiples constructores anotados.
También tenga en cuenta que Jackson ordena a los creadores por prioridad , por ejemplo, en este código:
@JsonCreator
public Phone(@JsonProperty("value") String value) {
this.value = value;
}
@JsonCreator
public Phone(Map<String, Object> properties) {
value = (String) properties.get("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");
Esta vez Jackson no generará un error, pero Jackson solo usará el constructor delegadoPhone(Map<String, Object> properties)
, lo que significa que Phone(@JsonProperty("value") String value)
nunca se usa.