EDIT: Siehe, in einem Blog-Beitrag der Betreuer von Jackson scheint es, dass 2.12 Verbesserungen in Bezug auf die Konstruktorinjektion sehen könnte. (Die aktuelle Version zum Zeitpunkt dieser Bearbeitung ist 2.11.1)
Verbessern Sie die automatische Erkennung von Konstruktorerstellern, einschließlich der Lösung / Linderung von Problemen mit mehrdeutigen 1-Argument-Konstruktoren (Delegieren gegen Eigenschaften).
Dies gilt weiterhin für Jackson Databind 2.7.0.
Die Jackson @JsonCreator
Anmerkung 2.5 javadoc oder Jackson Anmerkungen Dokumentation Grammatik ( Konstruktor s und Factory - Methode s ) können in der Tat glauben , dass man markieren mehrere Konstrukteure.
Markierungsanmerkung, mit der Konstruktoren und Factory-Methoden definiert werden können, um neue Instanzen der zugeordneten Klasse zu instanziieren.
Wenn man sich den Code ansieht, in dem die Ersteller identifiziert werden, sieht es so aus, als würde Jackson überladene KonstruktorenCreatorCollector
ignorieren, da nur das erste Argument des Konstruktors überprüft wird .
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
ist der erste identifizierte Konstruktorersteller.
newOne
ist der überladene Konstruktorersteller.
Das bedeutet, dass ein solcher Code nicht funktioniert
@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");
Aber dieser Code wird funktionieren:
@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");
Dies ist ein bisschen hacky und möglicherweise nicht zukunftssicher .
Die Dokumentation ist vage, wie die Objekterstellung funktioniert. Nach dem, was ich aus dem Code entnehme, ist es jedoch möglich, verschiedene Methoden zu mischen:
Zum Beispiel kann eine statische Factory-Methode mit Anmerkungen versehen werden @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");
Es funktioniert, aber es ist nicht ideal. Am Ende könnte es sinnvoll sein, z. B. wenn der JSON so dynamisch ist, sollte man vielleicht versuchen, einen Delegate-Konstruktor zu verwenden, um Nutzlastvariationen viel eleganter zu handhaben als mit mehreren kommentierten Konstruktoren.
Beachten Sie auch, dass Jackson die Ersteller nach Priorität ordnet , beispielsweise in diesem Code:
@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");
Dieses Mal wird Jackson keinen Fehler auslösen, aber Jackson wird nur den Delegate- Konstruktor verwenden Phone(Map<String, Object> properties)
, das heißt, der Phone(@JsonProperty("value") String value)
wird nie verwendet.