2013-04-10 3 views
50

Ich versuche, eine Instanz dieser Klasse deserialisieren mit Jackson 1.9.10:Wie eine Klasse mit überladenen Konstruktoren deserialisieren JsonCreator mit

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 ich folgendes

erhalten Widersprüchliche Property-basierte Ersteller: hatten bereits ... {interface org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator()}], gefunden ..., Anmerkungen: {interface org.codehaus. jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator()}]

Gibt es eine Möglichkeit, eine Klasse mit überladenen Konstruktoren mit Jackson zu deserialisieren?

Dank

+4

Wie die Antwort darauf hinweist, nein, müssen Sie nur einen Konstruktor angeben. In Ihrem Fall, lassen Sie den, der mehrere Argumente braucht, das wird gut funktionieren. "Fehlende" Argumente werden NULL (für Objekte) oder Standardwerte (für Primitive) annehmen. – StaxMan

+0

Danke. Mehrere Konstruktoren zuzulassen wäre jedoch eine nette Funktion. Eigentlich ist mein Beispiel ein bisschen erfunden. Das Objekt, das ich zu verwenden versuche, hat tatsächlich völlig verschiedene Argumentlisten, eine ist normal erstellt, die andere ist mit einem Throwable erstellt ... Ich werde sehen, was ich tun kann, vielleicht mit einem leeren Konstruktor und Getter/Setter für die Throwable – geejay

+0

Ja, ich bin sicher, es wäre nett, aber Regeln können mit verschiedenen Permutationen ziemlich komplex werden. Es ist immer möglich, RFEs für neue Funktionen und Funktionen zu speichern. – StaxMan

Antwort

83

Obwohl seine nicht richtig dokumentiert, können Sie einen Schöpfer haben nur pro Typ. Sie können beliebig viele Konstruktoren in Ihrem Typ haben, aber nur einer von ihnen sollte eine @JsonCreator Annotation haben.

+20

Das ist ein ziemlich wichtiges Detail ... – Gus

33

Dies gilt immer noch für Jackson Databind 2.7.0.

Die Jackson @JsonCreator annotation 2.5 javadoc oder Jackson annotations documentation Grammatik (Konstruktor s und Factory-Methode s) lassen dass man Marke mehrere Konstrukteure glauben in der Tat.

Marker-Annotation, die zum Definieren von Konstruktoren und Factory-Methoden verwendet werden kann, die zum Instanziieren neuer Instanzen der zugeordneten Klasse verwendet werden.

auf den Code, wo die Schöpfer identifiziert werden, es sieht aus wie die Jackson CreatorCollector ist ladenen Konstruktoren ignorieren, weil es nur checks the first argument of the 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 ist der erste identifizierte Konstruktor Schöpfer.
  • newOne ist der überladene Konstruktor Ersteller.

, dass dieser Code wie die bedeutet, dass

@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 funktioniert nicht:

@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 zukunftssicher nicht sein kann.


Die Dokumentation ist vage darüber, wie die Objekterstellung funktioniert; obwohl von dem, was ich aus dem Code zu sammeln, dann ist es, dass es möglich ist, verschiedene Methoden zu mischen:

Zum Beispiel kann man eine statische Factory-Methode kommentierte hat mit @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 die JSON ist dynamische dann sollte man vielleicht einen Delegate-Konstruktor verwenden, um Nutzlast Variationen viel eleganter als mit mehreren kommentierten Konstruktoren zu behandeln.

Beachten Sie auch, dass Jackson orders creators by priority, zum Beispiel 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 Jackson wird einen Fehler nicht erhöhen, aber Jackson wird nur die Verwendung Delegierten Konstruktor Phone(Map<String, Object> properties), das heißt die Phone(@JsonProperty("value") String value) ist nie benutzt.

+3

IMHO sollte dies die akzeptierte Antwort sein, weil es vollständige Erklärung mit nettem Beispiel liefert – matiou

Verwandte Themen