2014-10-05 13 views
5

Während ich an einer Android-App arbeitete, hatte ich ein Problem, als ich eine ultimative generische Methode zum Senden von HTTP-Anfragen (mit loopj) und Deserialisieren (mit Gson) erstellen wollte.Gson fromJson deserialize generics

Wie Sie vielleicht wissen, während gson.fromJson verwenden, können Sie nicht tun, wie folgt:

gson.fromJson(responseBody, new TypeToken<T>() {}.getType()); 

oder

gson.fromJson(responseBody, new TypeToken<ArrayList<T>>() {}.getType()) 

Statt eigentliche Objekt (oder die Liste der tatsächlichen Objekte in zweiter Fall), die Sie haben übergeben als T, erhalten Sie LinkedTreeMap Objekt (oder Liste der LinkedTreeMap Objekte).

Wenn Sie ein generisches Objekt (-s) weiterhin deserialisieren möchten, lesen Sie die Antwort unten.

+ Bonus: ultimate generische Lösung für loopj + Gson

Vielen Dank für Autoren und Kommentatoren dieser Beiträge:

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

How to get concrete type of a generic interface

Java Type Generic as Argument for GSON

Antwort

5

Deserialisieren JSON-Objekt zum generischen Typ Java-Objekt

Zuerst müssen wir die eigentliche Klasse des generischen Typs T bekommen.

Wir können es tun, indem Sie die Klasse selbst übergeben (Class<T> cl) oder indem Sie Klasse vom Objekt mit generischem Typ (SomeObject<T> someObjectWithGenericType) erhalten. Ich werde den zweiten Fall in Beispielen verwenden.

Dann müssen wir ein spezielles Objekt der Klasse Element<T> erstellen, das Gson sagt, welche Klasse für die Deserialisierung verwendet werden soll.

public <T> T getObject(String json, SomeObject<T> someObjectWithGenericType) { 
    Class cl = getTypeClassOfObject(someObjWithGenericType); 
    T object = gson.fromJson(json, new Element<T>(cl)); 
    return object; 
} 

private Class getTypeClassOfObject(Object obj) { 
    return (Class) ((ParameterizedType) obj.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
} 

private class Element<T> implements ParameterizedType { 

    private Class<T> cl; 

    public Element(Class<T> cl) { 
     this.cl = cl; 
    } 

    public Type[] getActualTypeArguments() { 
     return new Type[] {cl}; 
    } 

    public Type getRawType() { 
     return cl; 
    } 

    public Type getOwnerType() { 
     return null; 
    } 
} 

Wenn Ihr SomeObject<T> eine Schnittstelle (vielleicht ein Rückruf, wie Sie später in loopj Beispielen sehen werden), können Sie diese Methode verwenden, statt getTypeClassOfObject:

private Class getTypeClassOfInterfaceObject(Object obj) { 
    return (Class) ((ParameterizedType) obj.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]; 
} 

Deserialisieren JSON-Array zur Liste von generischen Java-Objekten

Gleiche Idee, aber wir haben eine andere spezielle Klasse, um Gson mit Deserialisierung zu helfen:

public <T> List<T> getList(String json, SomeObject<T> someObjectWithGenericType) { 
    Class cl = getTypeClassOfObject(someObjWithGenericType); 
    List<T> list = gson.fromJson(json, new ListWithElements<T>(cl)); 
    return list; 
} 

private class ListWithElements<T> implements ParameterizedType { 

    private Class<T> elementsClass; 

    public ListWithElements(Class<T> elementsClass) { 
     this.elementsClass = elementsClass; 
    } 

    public Type[] getActualTypeArguments() { 
     return new Type[] {elementsClass}; 
    } 

    public Type getRawType() { 
     return List.class; 
    } 

    public Type getOwnerType() { 
     return null; 
    } 
} 

BONUS

Wie Sie hier sehen someObjectWithGenericType wird ein Callback mit generischem Typ T sein. Obwohl ich loopj verwende, bin ich mir sicher, dass jeder andere asynchrone HTTP-Client verwendet werden kann, um dieselben Ergebnisse zu erzielen.

loopj + Gson mit Generika: Objekt

public <T> void getObject(String url, HashMap<String, String> paramsMap, final GetObjectCallback<T> callback) { 
    RequestParams params = convertParams(paramsMap); 
    client.get(url, params, new TextHttpResponseHandler() { 
     @Override 
     public void onSuccess(int statusCode, Header[] headers, String responseBody) { 
      try { 
       Class cl = getTypeClassOfInterfaceObject(callback); 
       T object = gson.fromJson(responseBody, new Element<T>(cl)); 
       if (object != null) { 
        callback.onSuccess(object); 
       } else { 
        callback.onFailure(); 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
       callback.onFailure(); 
      } 
     } 

     @Override 
     public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) { 
      error.printStackTrace(); 
      callback.onFailure(); 
     } 
    }); 
} 

private RequestParams convertParams(HashMap<String, String> paramsMap) { 
    RequestParams params = new RequestParams(); 
    if (paramsMap != null) { 
     for (String key : paramsMap.keySet()) { 
      params.put(key, paramsMap.get(key)); 
     } 
    } 
    return params; 
} 

public interface GetObjectCallback<T> { 
    void onSuccess(T item); 
    void onFailure(); 
} 

loopj + Gson mit Generika: Liste

public <T> void getList(String url, HashMap<String, String> paramsMap, final GetListCallback<T> callback) { 
    RequestParams params = convertParams(paramsMap); 
    client.get(url, params, new TextHttpResponseHandler() { 
     @Override 
     public void onSuccess(int statusCode, Header[] headers, String responseBody) { 
      try { 
       Class cl = getTypeClassOfInterfaceObject(callback); 
       List<T> list = gson.fromJson(responseBody, new ListWithElements<T>(cl)); 
       if (list != null) { 
        callback.onSuccess(list); 
       } else { 
        callback.onFailure(); 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
       callback.onFailure(); 
      } 
     } 

     @Override 
     public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) { 
      error.printStackTrace(); 
      callback.onFailure(); 
     } 
    }); 
} 

public interface GetListCallback<T> { 
    void onSuccess(List<T> list); 
    void onFailure(); 
} 

Verbrauch: Objekt

api.getObject(URL, paramsMap, new GetObjectCallback<NewsItem>() { 
    @Override 
    public void onSuccess(NewsItem item) { 
     // do something 
    } 

    @Override 
    public void onFailure() { 
     // do something 
    } 
}); 

Verbrauch: Liste

api.getList(URL, paramsMap, new GetListCallback<Comment>() { 
    @Override 
    public void onSuccess(List<Comment> list) { 
     // do something 
    } 

    @Override 
    public void onFailure() { 
     // do something 
    } 
}); 

Alle Verbesserungen sind sehr willkommen!

Verwandte Themen