web-dev-qa-db-ja.com

動的なArrayListアイテムタイプを持つGson TypeToken

私はこのコードを持っています:

Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);

JSON文字列をオブジェクトのListに変換します。しかし、今では、_ArrayListだけでなく、実行時に定義される動的な型でこのmyClassを持ちたいです。

ArrayListの項目タイプはreflectionで定義されます。

私はこれを試しました:

    private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
        Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
        return typeOfObjectsListNew;
    }

しかし、それは機能しません。これは例外です。

Java.sql.SQLException: Fail to convert to internal representation: {....my json....}
64
Amin Sh

提案している構文は無効です。以下

new TypeToken<ArrayList<Class.forName(MyClass)>>

型名が必要な場所でメソッド呼び出しを渡そうとしているため、無効です。

以下

new TypeToken<ArrayList<T>>() 

ジェネリック(型消去)とリフレクションの仕組みのために不可能です。 Class#getGenericSuperclass()は以下を行うため、TypeTokenハック全体が機能します。

このクラスによって表されるエンティティ(クラス、インターフェース、プリミティブ型、またはvoid)の直接のスーパークラスを表すTypeを返します。

スーパークラスがパラメーター化された型である場合、返されるTypeオブジェクトは、ソースコードで使用される実際の型パラメーターを正確に反映する必要があります。

言い換えると、ArrayList<T>が見つかった場合、それはParameterizedTypeであり、型変数Tが持っていたであろうコンパイル時の値を抽出することはできません。

TypeParameterizedTypeは両方ともインターフェースです。独自の実装のインスタンスを提供できます。

41

Gson 2.8.0以降では、 TypeToken#getParameterized(Type rawType, Type... typeArguments) を使用してTypeTokenを作成できます。その後、getType()を使用してください。

例えば:

TypeToken.getParameterized(ArrayList.class, myClass).getType()
43
oldergod

オプション1-Java.lang.reflect.ParameterizedTypeを自分で実装し、Gsonに渡します。

private static class ListParameterizedType implements ParameterizedType {

    private Type type;

    private ListParameterizedType(Type type) {
        this.type = type;
    }

    @Override
    public Type[] getActualTypeArguments() {
        return new Type[] {type};
    }

    @Override
    public Type getRawType() {
        return ArrayList.class;
    }

    @Override
    public Type getOwnerType() {
        return null;
    }

    // implement equals method too! (as per javadoc)
}

それから単に:

Type type = new ListParameterizedType(clazz);
List<T> list = gson.fromJson(json, type);

javadoc に従って、equalsメソッドも実装する必要があることに注意してください。

オプション2-(これをしないでください)gson internal ...を再利用します.

これは、少なくともGson 2.2.4でも機能します。

Type type = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
29
MateSzvoboda

これは私のために働いた:

public <T> List<T> listEntity(Class<T> clazz)
        throws WsIntegracaoException {
    try {
        // Consuming remote method
        String strJson = getService().listEntity(clazz.getName());

        JsonParser parser = new JsonParser();
        JsonArray array = parser.parse(strJson).getAsJsonArray();

        List<T> lst =  new ArrayList<T>();
        for(final JsonElement json: array){
            T entity = GSON.fromJson(json, clazz);
            lst.add(entity);
        }

        return lst;

    } catch (Exception e) {
        throw new WsIntegracaoException(
                "WS method error [listEntity()]", e);
    }
}
7
Rodrigo Tavares

これは、グアバのより強力な TypeToken を使用して実行できます。

private static <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
    return new TypeToken<ArrayList<T>>() {}
            .where(new TypeParameter<T>() {}, type)
            .getType();
}
7
shmosel

Sun.reflect.generics.reflectiveObjects.ParameterizedTypeImplが機能します。カスタム実装の必要はありません

Type type = ParameterizedTypeImpl.make(List.class, new Type[]{childrenClazz}, null);
List list = gson.fromJson(json, type);

マップおよびその他のコレクションで使用できます。

ParameterizedTypeImpl.make(Map.class, new Type[]{String.class, childrenClazz}, null);

カスタムデシリアライザーでそれを使用する方法の素敵なデモは次のとおりです: Gson

5
varren

実際に行うことができます。最初にデータを解析してJsonArrayにし、次に各オブジェクトを個別に変換し、それをListに追加するだけです。

Class<T> dataType;

//...

JsonElement root = jsonParser.parse(json);
List<T> data = new ArrayList<>();
JsonArray rootArray = root.getAsJsonArray();
for (JsonElement json : rootArray) {
    try {
        data.add(gson.fromJson(json, dataType));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
return data;
3
Ovidiu Latcu