Google Gson-list <class>オブジェクトをデシリアライズしますか?(ジェネリックタイプ)


441

Google Gsonを介してリストオブジェクトを転送したいのですが、ジェネリック型を逆シリアル化する方法がわかりません。

私は見ての後にしようとした何(BalusCの答え):

MyClass mc = new Gson().fromJson(result, new List<MyClass>(){}.getClass());

しかし、Eclipseで「type new List(){}は継承された抽象メソッドを実装する必要があります...」というエラーが発生し、クイックフィックスを使用すると、20を超えるメソッドスタブのモンスターが発生します。

私はもっ​​と簡単な解決策があると確信していますが、それを見つけることができないようです!

編集:

今私が持っています

Type listType = new TypeToken<List<MyClass>>()
                {
                }.getType();

MyClass mc = new Gson().fromJson(result, listType);

ただし、「fromJson」行で次の例外が発生します。

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

JsonParseExceptionsをキャッチし、「結果」はnullではありません。

デバッガーでlistTypeをチェックし、次の情報を得ました。

  • リストタイプ
    • args = ListOfTypes
      • リスト= null
      • resolveTypes = Type [1]
    • ローダー= PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class(java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

そのため、「getClass」の呼び出しが適切に機能しなかったようです。助言がありますか...?

Edit2:Gson User Guideで確認しました。Jsonへのジェネリック型の解析中に発生するはずのランタイム例外について言及しています。例のように、「間違った」(上には表示されていません)ことを行いましたが、その例外はまったく発生しませんでした。だから私はユーザーガイドが示唆するようにシリアライゼーションを変更しました。しかし、助けにはならなかった。

Edit3:解決しました。以下の私の回答を参照してください。


1
あなたが指摘した答えはを使用していTokenTypeます。その方法を試しましたか?
Nishant

ちょうど答えと同じヒントを得た。次回は例を詳しく見ていきます。;)
クラゲ

タイプトークンでリストの実装を試すことができますか?生の型は配列リストなので、配列リストを試してみてください。
uncaught_exceptions

回答:


954

ジェネリックコレクションを逆シリアル化するメソッド:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

コメントの数人がそれを言及したので、ここではTypeTokenクラスがどのように使用されているかの説明があります。構造は、new TypeToken<...>() {}.getType()コンパイル時の型(の間に捕捉<および>実行時に)java.lang.reflect.Typeオブジェクト。Class生の(消去された)型のみを表すことができるオブジェクトとは異なり、Typeは、ジェネリック型のパラメーター化されたインスタンス化を含め、Java言語の任意の型を表すことができます。

TypeTokenあなたが直接それを構築することになっていないため、クラス自体は、publicコンストラクタを持っていません。代わりに、常に無名のサブクラスを構築します(したがって、{}この式の必要な部分である)。

型の消去のため、TypeTokenクラスはコンパイル時に完全に既知の型のみをキャプチャできます。(つまりnew TypeToken<List<T>>() {}.getType()、型パラメーターに対して行うことはできませんT。)

詳細については、クラスのドキュメントを参照してくださいTypeToken


31
GSONの新しいバージョンでは、TypeTokenコンストラクターはパブリックではないため、ここではコンストラクターが表示されないというエラーが発生します。この場合、あなたは何をしなければなりませんか?
パブロ

8
GSONの実際のバージョン(2.2.4)を使用すると再び機能します。ここでコンストラクタにアクセスできます。

私のjsonオブジェクトはオブジェクトで始まり、次に目的のオブジェクトの配列が含ま { "myObjectArray":[ {....} , {....} , {....} ] }れています。モデルファイルを{....}作成しました。このネストされたコレクションコードで、ネストされた新しいオブジェクトファイルを作成せずにルート要素が配列であると想定しないようにするにはどうすればよいですか。
CQM 2014

7
以下のインポートが必要--- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken
Umair Saleem

4
YourClassがコードで修正されている場合、これは適切です。クラスが実行時に来るとどうなりますか?
jasxir

273

別の方法は、配列を型として使用することです。例:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

このようにして、Typeオブジェクトの手間を省き、リストが本当に必要な場合は、常に次の方法で配列をリストに変換できます。

List<MyClass> mcList = Arrays.asList(mcArray);

私見これはもっと読みやすいです。

そして、それを実際のリスト(変更可能、の制限を参照Arrays.asList())にするには、次のようにします。

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));

4
これは素晴らしい!リフレクションでどのように使用できますか?MyClass値がわからないので動的に定義されます!
アミンSh

1
nota:この場合、mcListが本格的なリストではないことに注意してください。多くのものが機能しません。
njzk2 14年

4
ジェネリックでそれを使用する方法?T[] yourClassList = gson.fromJson(message, T[].class);//型変数から選択できません
Pawel Cioch 2015

2
コメント時の@MateusViccariは、mcListこの回答ではへの呼び出しの結果のみでしたArrays.asList。このメソッドは、すべてではないにしてもほとんどのオプションのメソッドが実装されずに残り、例外をスローするリストを返します。たとえば、そのリストに要素を追加することはできません。後の編集が示唆するように、Arrays.asList制限があり、実際にそれをラップするとArrayList、多くの場合により役立つリストを取得できます。
njzk2 2017年

2
実行時に任意の要素型の配列型を構築する必要がある場合Array.newInstance(clazz, 0).getClass()は、David Woodの回答で説明されているように使用できます。
Daniel Pryden 2018年

31

この投稿を参照してください。 GSONの引数としてのJavaタイプGeneric

これにはもっと良い解決策があります。リストのラッパークラスは次のとおりです。ラッパーはリストの正確なタイプを格納できます。

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

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

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

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

そして、コードは単純にすることができます:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}

なにmEntity.rulePattern
Al Lelopath 2015

これはテスト用のサンプルオブジェクトです。あなたはそれを気にする必要はありません。toListメソッドを使用すると、すべてがうまくいきます。
Happier 2015

@HappierこのGson 2.8.2を実装しようとしていますが、動作していないようです。万が一 stackoverflow.com/questions/50743932/...あなたが見て、私は私が行方不明です何を知らせることができます
Praveenさん

1
@Praveen 2.8.2でこの方法を試しましたが、それはオリジナルとして機能します。
Happier

31

以降Gson 2.8、次のようなutil関数を作成できます

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

使用例

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);

2
TypeToken#getParameterized匿名のサブクラスを使用したハックよりも
はるかに

これは受け入れられる答えでなければなりません。少なくとも新しいバージョンの場合
-ccpizza

2
これは完璧な答えです。私の問題を解決します。
donmj

私はあなたのメソッドを「現状のまま」コピーしましたが機能しません:コンパイラは「メソッドgetParameterized(Class <List>、Class <T>)はタイプTypeTokenに対して未定義です」と言います。私は私のGsonバージョン(2.8.0)およびドキュメントをチェックし、すべてがこちら側の罰金です...私は罰金を作品@Happierソリューションを使用して終了
leguminator

@leguminator TypeTokenを正しくインポートしましたか?あなたはjavaまたはkotlinを使用しています。もう一度テストしてみます
ファンヴァンリン

26

Wep、同じ結果を達成する別の方法。読みやすさのために使用しています。

この読みにくい文を実行する代わりに:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

オブジェクトのリストを拡張する空のクラスを作成します。

public class YourClassList extends ArrayList<YourClass> {}

そして、JSONを解析するときに使用します。

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);

9

Kotlinの場合:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

または、ここに便利な関数があります:

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

次に、使用するには:

val type = typeOfList<YourMagicObject>()

私はあなたのコードを使用して、具体化された型を使用してこの関数を作成しました:inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!!そしてそれをリスト型で呼び出します:buildType<List<YourMagicObject>>()
coffeemakr

@coffeemakrここでは具体化された型は必要ありません。
チャドビンガム

ああ。しかし、なぜArrayListの型トークンを作成buildTypeし、ジェネリック型で関数を呼び出すのですか?これはタイプミスですか?-これにより、ArrayList <ArrayList <YourMagicObject >>が作成されます
coffeemakr

@coffeemakrああ、そう。タイプミス
チャドビンガム

7
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

例:

getList(MyClass[].class, "[{...}]");

いいね。しかし、この重複DevNGます。■上記の答えは、2年前に書かれたstackoverflow.com/a/17300003/1339923(このアプローチへの注意事項については、その答えを読む)
Lambart

6

私の元の質問に答えるので、私はdoc_180の回答を受け入れましたが、誰かがこの問題に再び遭遇した場合は、私の質問の後半にも答えます。

私が説明したNullPointerErrorは、リスト自体とは関係ありませんが、その内容です!

「MyClass」クラスには「引数なし」コンストラクタはなく、スーパークラスコンストラクタもありませんでした。MyClassとそのスーパークラスに単純な "MyClass()"コンストラクターを追加すると、doc_180で提案されているListのシリアル化と逆シリアル化を含め、すべてが正常に機能しました。


1
抽象クラスのリストがある場合、同じエラーが発生します。これは、「クラスをインスタンス化できません」というGSONの一般的なエラーメッセージだと思います。
ドリュー

コンストラクタの追加に関するヒントは、なぜすべてのnull値があるのか​​を理解するのに役立ちました。JSON文字列に "To"や "From"などのフィールド名がありましたが、オブジェクトの対応するフィールドは小文字の "to"と "from"だったため、スキップされました
Rune

4

以下は、動的に定義された型で機能するソリューションです。コツは、Array.newInstance()を使用して適切なタイプの配列を作成することです。

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}

class.cast()へのキャストによって引き起こされるチェックされていない警告を回避するために使用した場合、この答えはより良いでしょう(T)。または、さらに良いことに、キャストを気にせずArrays.asList()、配列からに変換するようなものを使用してくださいList<T>。また、長さを渡す必要はありません。Array.newInstance()サイズが0の配列で十分getClass()です。
Daniel Pryden 2018年

提案いただきありがとうございます。提案された変更を行い、上記の投稿を更新しました。
David Wood

2

もう1つの可能性を追加したいと思います。TypeTokenを使用せず、jsonオブジェクトの配列をArrayListに変換する場合は、次のように進めます。

あなたのjson構造が次のような場合:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

そしてあなたのクラス構造は次のようなものです:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

その後、次のように解析できます:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

これで、classNameオブジェクトの各要素にアクセスできます。


1

Gsonの「タイプ」クラスの理解については、例2を参照してください。

例1:このdeserilizeResturantでは、Employee []配列を使用して詳細を取得します

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

例2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}

0

kays1からの回答は気に入りましたが、実装できませんでした。だから私は彼のコンセプトを使って自分のバージョンを作った。

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

使用法:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);

コンパイル時にTを使用しようとしているので、これは確かに機能しません。これは効果的にStringMapのリストに逆シリアル化しますか?
JHH 2017

0

私の場合、@ uncaught_exceptionsの回答が機能しませんList.classでしたjava.lang.reflect.Type。代わりに次のように使用する必要がありました。

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.