web-dev-qa-db-ja.com

GSONを使用したジェネリック型の逆シリアル化

私のAndroidアプリケーション(Gsonライブラリを使用)でのJson Deserializationの実装に問題があります

私はこのようなクラスを作りました

public class MyJson<T>{
    public List<T> posts;
}

そして、逆シリアル化の呼び出しは次のとおりです。

public class JsonDownloader<T> extends AsyncTask<Void, Void, MyJson<T>> {
...
protected MyJson<T> doInBackground(Void... params) {
  ...
    Reader reader = new InputStreamReader(content);
    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>() {}.getType();
    result = gson.create().fromJson(reader, collectionType);
  ...
  }
}

問題は、呼び出し後のresult.postsリストが、MyJsonオブジェクトの代わりにLinkedTreeMapオブジェクトの配列を1つ保持していることです(正しい値であるため、問題は逆シリアル化です)。 Tの代わりにMyObjectを使用すると、すべてが正常に実行され、MyObjectが正しくなります。

カスタムデシリアライザーを作成せずにデシリアライズ呼び出しを実装する方法はありますか?

44
VizGhar

直列化復元時にTのタイプを指定する必要があります。 Listがインスタンス化するpostsを知らなかった場合、GsonTypeはどのように作成されますか? Tを永久に維持することはできません。したがって、T型をClassパラメーターとして提供します。

ここで、postsのタイプはStringであると仮定すると、MyJson<String>をデシリアライズします(簡単にするためにString jsonパラメーターも追加しました。reader 従来通り):

doInBackground(String.class, "{posts: [\"article 1\", \"article 2\"]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // ["article 1", "article 2"]
    return myJson;
}

同様に、MyJson of Booleanオブジェクトをデシリアライズするには

doInBackground(Boolean.class, "{posts: [true, false]}");

protected MyJson<T> doInBackground(Class<T> type, String json, Void... params) {

    GsonBuilder gson = new GsonBuilder();
    Type collectionType = new TypeToken<MyJson<T>>(){}.getType();

    MyJson<T> myJson = gson.create().fromJson(json, collectionType);

    System.out.println(myJson.getPosts()); // [true, false]
    return myJson;
}

私の例ではMyJson<T>を次のように仮定しています

public class MyJson<T> {

    public List<T> posts;

    public List<T> getPosts() {
        return posts;
    }
}

したがって、List<MyObject>をデシリアライズする場合は、次のようにメソッドを呼び出します。

// assuming no Void parameters were required
MyJson<MyObject> myJson = doInBackground(MyObject.class);
30
Ravi Thapliyal

やってみました?

gson.create().fromJson(reader, MyJson.class);

[〜#〜] edit [〜#〜]

this postを読んだ後、Typeの使用は正しいようです。あなたの問題はTの使用だと思います。 Javaでは型消去があります。これは、実行時にTのすべてのインスタンスがObjectに置き換えられることを覚えておく必要があります。 GSONを渡すのは本当にMyJson<Object>です。<T>の代わりに具体的なクラスでこれを試してみたら、うまくいくと思います。

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

24
John B

したがって、試行錯誤の後、上記の答えは私にとってうまくいきませんでした、それが私のコードの終わりです:

public class AbstractListResponse<T> {
    private List<T> result;

    public List<T> getResult() {
        return this.result;
    }
}

ここで重要な部分は、左側の「<T>」を含むメソッドシグネチャです。

protected <T> AbstractListResponse<T> parseAbstractResponse(String json, TypeToken type) {
    return new GsonBuilder()
            .create()
            .fromJson(json, type.getType());
}

Gsonを呼び出すと、メソッドは汎用オブジェクトのTypeTokenを受け取ります。

TypeToken<AbstractListResponse<MyDTO>> typeToken = new TypeToken<AbstractListResponse<MyDTO>>() {};
AbstractListResponse<MyDTO> responseBase = parseAbstractResponse(json, typeToken);

そして最後に、TypeTokenはMyDTOまたは単純なオブジェクトでさえMyDTOを使用できます。

3
Davi Alves

私がやったようにコトリンと苦労している人のために、私はこのように働く方法を見つけました

val type = object : TypeToken<MyJson<MyObject>>() { }.type
val response = gson.fromJson<MyJson<MyObject>>(reader, type)

ジェネリック関数を呼び出すには、関数の名前の後に呼び出しサイトで型引数が必要であることに注意してください( here に表示)

1
tudor