Ich versuche, eine Instanz dieser Klasse mit Jackson 1.9.10 zu deserialisieren:
public class Person {
@JsonCreator
public Person(@JsonProperty("name") String name,
@JsonProperty("age") int age) {
// ... person with both name and age
}
@JsonCreator
public Person(@JsonProperty("name") String name) {
// ... person with just a name
}
}
Wenn ich das versuche, bekomme ich folgendes
Widersprüchliche eigenschaftsbasierte Ersteller: hatten bereits ... {Schnittstelle org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}], angetroffen ..., Anmerkungen: {Schnittstelle org.codehaus. jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}]
Gibt es eine Möglichkeit, eine Klasse mit überladenen Konstruktoren mit Jackson zu deserialisieren?
Vielen Dank
Antworten:
Obwohl es nicht richtig dokumentiert ist, können Sie nur einen Ersteller pro Typ haben. Sie können so viele Konstruktoren in Ihrem Typ haben, wie Sie möchten, aber nur einer von ihnen sollte eine
@JsonCreator
Anmerkung enthalten.quelle
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)
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.Wenn man sich den Code ansieht, in dem die Ersteller identifiziert werden, sieht es so aus, als würde Jackson überladene Konstruktoren
CreatorCollector
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"); // raise error here 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:
// 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");
Dieses Mal wird Jackson keinen Fehler auslösen, aber Jackson wird nur den Delegate- Konstruktor verwenden
Phone(Map<String, Object> properties)
, das heißt, derPhone(@JsonProperty("value") String value)
wird nie verwendet.quelle
Wenn ich richtig verstanden habe, was Sie erreichen möchten, können Sie es ohne Konstruktorüberlastung lösen .
Wenn Sie nur Nullwerte in die Attribute einfügen möchten, die in einem JSON oder einer Map nicht vorhanden sind, können Sie Folgendes tun:
@JsonIgnoreProperties(ignoreUnknown = true) public class Person { private String name; private Integer age; public static final Integer DEFAULT_AGE = 30; @JsonCreator public Person( @JsonProperty("name") String name, @JsonProperty("age") Integer age) throws IllegalArgumentException { if(name == null) throw new IllegalArgumentException("Parameter name was not informed."); this.age = age == null ? DEFAULT_AGE : age; this.name = name; } }
Das war mein Fall, als ich Ihre Frage fand. Ich habe einige Zeit gebraucht, um herauszufinden, wie ich es lösen kann. Vielleicht war es das, was Sie versucht haben. @ Brice-Lösung hat bei mir nicht funktioniert.
quelle
Wenn es Ihnen nichts ausmacht, etwas mehr Arbeit zu erledigen, können Sie die Entität manuell deserialisieren:
@JsonDeserialize(using = Person.Deserializer.class) public class Person { public Person(@JsonProperty("name") String name, @JsonProperty("age") int age) { // ... person with both name and age } public Person(@JsonProperty("name") String name) { // ... person with just a name } public static class Deserializer extends StdDeserializer<Person> { public Deserializer() { this(null); } Deserializer(Class<?> vc) { super(vc); } @Override public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { JsonNode node = jp.getCodec().readTree(jp); if (node.has("name") && node.has("age")) { String name = node.get("name").asText(); int age = node.get("age").asInt(); return new Person(name, age); } else if (node.has("name")) { String name = node.get("name").asText(); return new Person("name"); } else { throw new RuntimeException("unable to parse"); } } } }
quelle