2014-10-05 19 views
5

Podczas pracy nad aplikacją na Androida napotkałem problem, gdy chciałem stworzyć ostateczne ogólne metody wysyłania żądań HTTP (z loopj) i deserializować je (z Gsonem).Gson fromJson deserialize generics

Jak wiadomo podczas korzystania gson.fromJson nie można zrobić tak:

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

lub

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

Zamiast rzeczywistego obiektu (lub listy rzeczywistych obiektów w 2 przypadku), że masz przekazany jako T, otrzymasz obiekt LinkedTreeMap (lub listę obiektów LinkedTreeMap).

Jeśli chcesz nadal deserializować obiekt typu ogólnego (-s), zobacz odpowiedź poniżej.

+ Bonus: ostateczny generic rozwiązanie dla loopj + gson

Dzięki dla autorów i komentujących tych stanowisk:

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

Odpowiedz

5

deserializacji JSON obiektu do ogólnego typu obiekt java

Najpierw musimy uzyskać rzeczywistą klasę rodzaju ogólnego T.

Możemy to zrobić, przekazując samą klasę (Class<T> cl) lub pobierając klasę z obiektu o typie ogólnym (SomeObject<T> someObjectWithGenericType). W przykładach wykorzystam drugi przypadek.

Następnie musimy stworzyć specjalny obiekt klasy Element<T>, który powie Gsonowi, której klasy użyć do deserializacji.

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

Jeśli Twój SomeObject<T> jest interfejsem (być może zwrotna, jak widać w przykładach loopj później), można użyć tej metody zamiast getTypeClassOfObject:

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

deserializacji JSON tablicę do listy ogólnych typów obiektów java

Taki sam pomysł, ale mamy inną klasę specjalną, która pomaga Gsonowi w deserializacji:

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

Jak widać tutaj someObjectWithGenericType ma być zwrotna z uniwersalnym typem T. Mimo że używam loopj, jestem pewien, że każdy inny asynchroniczny klient http może być użyty do osiągnięcia tego samego rezultatu.

loopj + Gson z rodzajowych: obiekt

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

Zastosowanie: obiekt

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

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

Zastosowanie: lista

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

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

Wszelkie ulepszenia są bardzo mile widziane!

Powiązane problemy