web-dev-qa-db-ja.com

Google Gson - リスト<class>オブジェクトのシリアル化を解除しますか? (ジェネリック型)

リストオブジェクトをGoogle Gson経由で転送したいのですが、ジェネリック型をデシリアライズする方法がわかりません。

これ (BalusCの答え)を見た後に私が試したこと:

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

しかし、Eclipseで "new list(){}型は継承された抽象メソッドを実装する必要があります..."というエラーが表示されます。

もっと簡単な解決策があることは確かですが、見つけることができないようです。

編集する

今私が持っています

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)

I do JsonParseExceptionsをキャッチし、 "result"がnullではありません。

私はデバッガでlistTypeをチェックして、そして以下を得ました:

  • リストタイプ
    • args = ListOfTypes
      • list = null
      • olvedTypes =タイプ[1]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class(Java.util.ArrayList)
    • rawTypeName = "Java.util.ArrayList"

そのため、 "getClass"呼び出しは正しく機能しませんでした。助言がありますか...?

Edit2: Gsonユーザーガイド をチェックしました。 Jsonにジェネリック型を解析している間に発生するはずの実行時例外について言及しています。例にあるように、私はそれを「間違って」行った(上記には示されていない)が、その例外をまったく得なかった。そのため、ユーザーガイドの提案どおりにシリアル化を変更しました。しかし、助けにはならなかった。

編集3:解決、下の私の答えを見てください。

383
jellyfish

ジェネリックコレクションを逆シリアル化する方法

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クラス自体にはパブリックコンストラクタはありません。代わりに、匿名のサブクラスを構築する必要があります(したがって、この式の必要な部分である{})。

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

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

854

もう1つの方法は、型として配列を使用することです。

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

こうすれば、Typeオブジェクトの面倒な操作をすべて避けることができます。本当にリストが必要な場合は、次の方法で常に配列をリストに変換できます。

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

私見これははるかに読みやすいです。

そしてそれを実際のリストにするには(これは変更することができます。Arrays.asList()の制限を参照してください)、次のようにしてください。

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

この記事を参照してください。 GSONの引数として汎用のJava型

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

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));
}
27
Happier

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);
21
Roc Boronat

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);
11
Phan Van Linh
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, "[{...}]");
7
kayz1

コトリンのために:

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

または、これは便利な機能です。

fun <T> buildType(): Type {
    return object : TypeToken<ArrayList<T>>() {}.type
}

その後、使用する:

val type = buildType<YourMagicObject>()
6
Chad Bingham

それが私の最初の質問に答えるとき、私はdoc_180の答えを受け入れました、しかし、誰かがこの問題に再び遭遇するならば、私も私の質問の後半に答えるつもりです:

私が説明したNullPointerErrorは、List自体とは無関係ですが、その内容とは関係ありません。

"MyClass"クラスは "no args"コンストラクタを持っておらず、どちらもそのスーパークラスを持っていませんでした。 MyClassとそのスーパークラスに単純な "MyClass()"コンストラクタを追加すると、doc_180で提案されているようにListのシリアライゼーションとデシリアライゼーションを含め、すべてうまくいきました。

6
jellyfish

これは動的に定義された型を扱う解決策です。トリックは、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; 
}
4
David Wood

もう一つの可能​​性を加えたいと思います。 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オブジェクトの各要素にアクセスできます。

2
Apurva Sharma

Gsonの 'Type'クラスの理解については例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());
    }
}
1
Ram Patro

私の場合、@ 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);
0
Andrei

私は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);
0
mike83_dev