2014-10-05 9 views
5

Mentre lavoravo su un'app per Android ho riscontrato un problema quando volevo creare un metodo generico definitivo per inviare richieste HTTP (con loopj) e deserializzare (con Gson).Gson fromJson deserialize generics

Come forse sapete durante l'utilizzo di gson.fromJson non si può fare in questo modo:

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

o

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

Invece di oggetto reale (o un elenco di oggetti reali in 2 ° caso) che hai passato come T, si otterrà l'oggetto LinkedTreeMap (o l'elenco di oggetti LinkedTreeMap).

Se si desidera ancora deserializzare l'oggetto di tipo generico (-s), consultare la risposta di seguito.

+ bonus: soluzione generica definitiva per loopj + GSON

Grazie per autori e commentatori di questi post:

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

risposta

5

deserializzazione JSON oggetto al tipo generico java object

Per prima cosa è necessario ottenere la classe effettiva del tipo generico T.

Possiamo farlo passando la classe stessa (Class<T> cl) o ottenendo la classe dall'oggetto con tipo generico (SomeObject<T> someObjectWithGenericType). Userò il secondo caso negli esempi.

Quindi è necessario creare un oggetto speciale di classe Element<T> che indicherà a GSON quale classe utilizzare per la deserializzazione.

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; 
    } 
} 

Se il SomeObject<T> è un'interfaccia (forse una richiamata, come si vedrà negli esempi loopj successive), è possibile utilizzare questo metodo al posto di getTypeClassOfObject:

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

deserializzazione JSON serie di elencare di oggetti java di tipo generico

Stessa idea, ma abbiamo una classe speciale diversa per aiutare Gson con deserializzazione:

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

Come vedrete qui someObjectWithGenericType sta per essere un callback con tipo generico T. Anche se uso loopj sono sicuro che qualsiasi altro client async http può essere utilizzato per ottenere gli stessi risultati.

loopj + GSON con i generici: oggetto

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 con i generici: lista

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(); 
} 

Usage: oggetto

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

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

Usage: lista

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

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

Eventuali miglioramenti sono i benvenuti!