2012-10-29 13 views
5

Ich versuche, eine Klasse zu machen, die den Jackson verwendet, um POJOs zu deserialisieren.Deserialisieren eines generischen Typs mit Jackson

Es sieht wie folgt aus ...

public class DeserialiserImp<T> implements Deserialiser<T> { 

     protected ObjectMapper objectMapper = new ObjectMapper(); 

     @Override 
     public T get(String content, Class clazz) throws IOException { 
      return (T) objectMapper.readValue(content, clazz); 
     } 

     @Override 
     public List<T> getList(String content, Class clazz) throws IOException { 
      return objectMapper.readValue(content, TypeFactory.collectionType(ArrayList.class, clazz)); 
     } 

    } 

Ich habe 2 Fragen zu dieser Umsetzung.

Die erste besteht darin, dass ich den Klassentyp in die Methoden übergebe, damit der Objektmapper den Typ kennt, der deserialisiert werden sollte. Gibt es einen besseren Weg, Generika zu verwenden?

Auch in der Get-Methode bin ich ein Objekt vom ObjektMapper zu T geworfen. Dies scheint besonders fiese Art und Weise es zu tun, wie ich T hier werfen muss und dann muss ich auch den Objekttyp aus der Methode, die ruft es an.

Ich benutze Roboguice in diesem Projekt, so wäre es nett, wenn ich den Typ durch Injektion ändern und dann das Objekt mit dem generischen Typ kommentieren könnte, den ich es zurückgeben muss. Ich lese über TypeLiteral und frage mich, ob es dieses Problem lösen könnte?

+0

Siehe http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak

Antwort

5

Also ich denke, ich habe es am Ende herausgefunden. Bitte kommentiere, wenn du etwas falsch machst mit dem was ich tue.

Die Schnittstelle wie so definiert ist ...

public interface Deserialiser<T> { 

    T get(String content) throws IOException; 

    List<T> getList(String content) throws IOException; 
} 

Die Implementierung der Schnittstelle wie das ist ...

public class DeserialiserImp<T> implements Deserialiser<T> { 

    private ObjectMapper objectMapper = new ObjectMapper(); 
    private final Class<T> klass; 

    @Inject 
    public DeserialiserImp(TypeLiteral<T> type){ 
     this.klass = (Class<T>) type.getRawType(); 
    } 

    @Override 
    public T get(String content) throws IOException { 
     return objectMapper.readValue(content, klass); 
    } 

    @Override 
    public List<T> getList(String content) throws IOException { 
     return objectMapper.readValue(content, TypeFactory.collectionType(ArrayList.class, klass)); 
    } 

} 

Ich binde das 2 wie so ..

bind(new TypeLiteral<Deserialiser<User>>(){}).annotatedWith(Names.named("user")).to(new TypeLiteral<DeserialiserImp<User>>(){}); 

Dann alles, was ich tun müssen, um es zu benutzen ist dies ...

@Inject 
@Named("user") 
private Deserialiser<User> deserialiserImp; 

public void test(String userString) { 
    User user = deserialiserImp.get(UserString); 
} 

Dieses Muster auch gut funktionieren könnte, wenn die Klasse als abstrakte Klasse in einem DAO-Objekt verwenden

This Artikel hat mir geholfen

5

Die erste besteht darin, dass ich den Klassentyp in die Methoden übergebe, so dass der Objektmapper den Typ kennt, der deserialisiert werden sollte. Gibt es einen besseren Weg mit Generika?

Leider nicht, und dies ist wegen Typ löschen.

Auch in der get-Methode mir ein Objekt aus dem Dies scheint besonders perfide Art und Weise, es zu tun, wie ich habe zu werfen T hier und dann auch objectMapper T. am Casting zurück geworfen Ich muß den Objekttyp von die Methode, die es aufruft.

Tun Sie dies statt:

@Override 
public T get(String content, Class<T> clazz) throws IOException { 
    return objectMapper.readValue(content, clazz); 
} 

Ich verwende Roboguice in diesem Projekt so wäre es schön, wenn ich die Art durch Injektion ändern könnte und dann mit Anmerkungen versehen das Objekt, das der generische Typ Ich muss es zurückgeben. Ich habe über TypeLiteral und gelesen und frage mich, ob es dieses Problem lösen könnte?

Ich verstehe nicht wirklich, was Sie erreichen wollen, aber da Sie die Klasse sowieso bestehen müssen, ist das noch möglich?

+0

Mein Punkt über die Verwendung von TypeLiteral zu kommen s aus dieser Frage http://stackoverflow.com/questions/3370641/how-does-guices-typeliteral-work Es heißt, dass "Der Trick, der hier verwendet wird, ist, dass die Signaturen von generischen Super-Typen in Unterklassen gespeichert sind und überleben also die Löschung. " Ich hoffe, den Typ nicht zu übergeben. – jiduvah

+1

Ok ich verstehe. Ich sehe hier keine praktische Lösung, Sie müssen den Typ irgendwo speichern (einfaches Class-Objekt als Feld, TypeLiteral usw. verwenden), um ihn zur Laufzeit abzurufen. Ich denke nicht, jedes Mal die Art zu übergeben ist ein großes Problem, da es von allen unmarshalling Bibliotheken benötigt wird. Ich wäre jedoch daran interessiert, einen anderen Ansatz zu sehen. –

+0

Nun, ich versuche, den Code sauberer zu machen. In dem Moment, in dem ich den Typ als ein generisches Objekt übergebe, wenn ich den deserialiserImp instanziiere, muss ich die Klasse in die Methode einbinden, dann muss ich auch das Objekt, das ich erhalte, umwandeln. Also das 3 mal referenziere ich den Typ. Es scheint sehr Messey – jiduvah

Verwandte Themen