2016-07-08 8 views
1

Ich muss einen benutzerdefinierten Deserializer für eine Klasse mit Generika schreiben. Ich konnte keinen Weg finden, dies zu tun, aber ich kann mir nicht vorstellen, dass ich der Einzige mit diesem Problem bin. Soweit ich durch sie gedacht haben, gäbe es zwei Möglichkeiten, dies zu implementieren, aber keiner von ihnen sind implementierbar:Jackson Deserializer für den generischen Typ

  1. im Konstruktor der Deserializer eine Klasse Parameter dem Deserializer Providing, funktioniert nicht weil bei der Registrierung des Deserializers die Beziehung zwischen Type.class und der Instanz des Deserializers mit der übergebenen Klasseninstanz verloren geht.

Zum Beispiel:

public class Foo<T> {} 
public class FooDeserializer<T> { 
    public FooDeserializer(Class<T> type) { ... } 
    ... 
} 
// Boilerplate code... 
module.addDeserializer(Foo.class, new FooDeserializer<Bar1>(Bar1.class)); 
module.addDeserializer(Foo.class, new FooDeserializer<Bar2>(Bar2.class)); 

Dies funktioniert nicht, wenn eine ObjectMapper Instanz eine Instanz von Foo bekommt, gibt es keine Typinformationen des generischen Parameters zur Verfügung (Typ Löschung), so dass es wählt einfach die letzter Deserializer registriert.

  1. Es hilft nicht, einen Verweis auf den generischen Typ in der Klasse beizubehalten, da eine instanziierte Version der Klasse nicht an den Deserializer übergeben werden kann (die Schnittstelle ist readValue (String, Klasse)).

Zum Beispiel:

String json = "..."; 
ObjectMapper mapper = ...; 
Foo<Bar1> foo = new Foo<>(Bar1.class); 
foo = mapper.readValue(json, Foo.class); // Can't pass empty foo instance with Class<?> field containing Bar1.class 

So etwas wie dies erforderlich wäre:

mapper.readValue(json, Foo.class, Bar1.class); // Doesn't exist in jackson 

Irgendwelche Vorschläge, wie dies zu tun?

EDIT: ich einen Weg gefunden, um das Problem zu lösen, aber es ist nicht eine saubere Lösung:

Ich erweitere die FooDeserializer mit einem Klasse-Feld den Typ des Foo generischen Parameter zu speichern. Jedes Mal, wenn ich einen JSON in eine neue Foo-Instanz deserialisieren möchte, erhalte ich eine neue ObjectMapper-Instanz (ich verwende ObjectMapper # copy auf einer vorkonfigurierten Instanz einer Factory) und gebe ihr ein neues Modul, das eine Instanz des FooDeserializer enthält der Klassenparameter (ich kenne den Typ zu diesem Zeitpunkt). Module, FooDeserializer und die ObjectMapper-Kopie sind kurzlebig, sie werden nur für diese einzelne Deserialisierungsaktion instanziiert. Wie gesagt, nicht sehr sauber, aber immer noch besser, als Foo viele Male zu unterlegen und für jeden einen Deserializer zu schreiben.

Beispiel:

public class FooDeserializer<T> extends StdDeserializer<T> { 
    private Class<T> type; 
    public FooDeserializer(Class<T> type) { this.type = type } 
    ... 
} 

// Meanwhile, before deserialization: 
ObjectMapper mapper = MyObjectMapperFactory.get().copy(); 
SimpleModule module = new SimpleModule(new Version(0,0,1,null,null,null); 
module.addDeserializer(Foo.class, new FooDeserializer(Bar1.class); 
mapper.addModule(module); 
Foo<Bar1> x = mapper.readValue(json, Foo.class); 

Wahrscheinlich dies in eine Dienstprogramm Methode setzt die uglyness zu verstecken.

+0

http://stackoverflow.com/questions/11664894/jackson-deserialize-using-generic-class kann helfen – Wilson

Antwort

0

Ich glaube nicht, dass Sie unseren eigenen benutzerdefinierten Deserializer schreiben müssen. Sie können diese Syntax zum Deserialisieren von Objekten verwenden, die Generics aus einem anderen Stack Overflow-Thread verwenden.

mapper.readValue(jsonString, new TypeReference<Data<String>>() {}); 

Paar Quellen Ihnen helfen: Jackson - Deserialize using generic class http://www.tutorialspoint.com/jackson/jackson_data_binding_generics.htm

+0

Ja, das könnte in einigen Situationen funktionieren. Mein Typ (Foo im Beispiel) implementiert jedoch Collection unter einigen anderen Schnittstellen. Daher verwendet jackson CollectionDeserializer, anstatt einfach auf die Felder der Foo-Klasse zu mappen. Dies funktioniert nicht, da CollectionDeserializer erwartet, dass der JSON mit einem '[' - Zeichen beginnt, aber mein JSON beginnt mit einem '{' - Zeichen, das JSON-Array der Sammlung ist im Root-JSON-Objekt verschachtelt. – nsommer

0

Sie benötigen keine eigenen Deserializer schreiben zu können. Ein generischer Typ kann deserialisiert werden, indem Jackson-Annotationen auf den Typ gesetzt werden, der als Parameter an die generische Klasse übergeben wird.Jetzt

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) 
@JsonTypeName("Bar1") 
class Bar1 { 

} 

, wenn Sie eine Instanz von Foo<Bar1> deserialisieren, Jackson Typ Parameter info in JSON gesetzt wird:
Beschriften Klasse Bar1 wie folgt. Dieser JSON kann dann genauso in die generische Klasse Foo.class deserialisiert werden, wie Sie eine andere Klasse deserialisieren würden.

ObjectMapper mapper = ...; 
Foo<Bar1> foo = new Foo<Bar1>(); 
String json = objectMapper.writeValueAsString(foo); 
foo = mapper.readValue(json, Foo.class); // no need to specify the type Bar1. 

Also, wenn jede Klasse, die als Parameter an Foo hat Anmerkungen auf sie übergeben werden kann, dann kann JSON werden, ohne deserialisiert die Typ-Parameter bei der Kompilierung zu kennen.

Bitte verweisen Sie Jackson Dokumentation auf Polymorphic Deserialization und Annotations.