15

サーバーに接続し、いくつかの引数に基づいて、GSONで指定された(ジェネリックを介して)クラスに解析されるjson-stringを取得するクラスを作成しています。

担当クラスの簡略版は次のようになります。

class Executor<T> {

    private Response<T> response;

    public void execute() {
        Type responseType = new TypeToken<Response<T>>() {}.getType();
        this.response = new Gson().fromJson(json, responseType);
    }

    public Response<T> getResponse() { return this.response; }

}

JSON-変数は次のようになります。)

逆シリアル化されたデータを格納するクラスは、次のようになります。

class Response<T> {

    private List<T> data = null;

    public List<T> getData() { return this.data; }

}

データが逆シリアル化されようとしているクラス:

public class Language {
    public String alias;
    public String label;
}

そして、実行されるコードは上記のクラスを利用します。

Executor<Language> executor = new Executor<Language();
List<Language> languages = executor.execute().getResponse().getData();
System.out.println(languages.get(0).alias); // exception occurs here

その結果、次の例外が発生します

ClassCastException:com.google.gson.internal.StringMapをsunnerberg.skolbibliotek.book.Languageにキャストできません

ヘルプや提案は大歓迎です!

4

3 に答える 3

42

簡単に言えば、 の作成を のTypeToken外に移動し、トークン ( ) を作成するときにインExecutorをバインドし、型トークンをコンストラクターに渡す必要があるということです。TResponse<T>new TypeToken<Response<Language>>() {}Executor

長い答えは次のとおりです。

型のジェネリックは、通常、型がジェネリック パラメーターをバインドしてコンパイルされる場合を除いて、実行時に消去されます。その場合、コンパイラはジェネリック型情報をコンパイル済みクラスに挿入します。それ以外の場合、それは不可能です。

たとえば、次のように考えてください。

List<Integer> foo = new ArrayList<Integer>();

class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();

コンパイル時に型がバインドされるため、Java は実行時に整数が含まれていることを認識 し、ジェネリック型情報がクラス ファイルに保存されます。ただし、 のジェネリック型情報は消去されるため、実行時に に sが含まれていると判断することは実際には不可能です。barIntegerArrayListIntegerListfoofooInteger

そのため、GSON で JSON データを解析する場合のように、通常は実行前に消去される状況でジェネリック型情報が必要になることがよくあります。IntegerListこのような状況では、(上記の例のように)コンパイル時にバインドされたときに型情報が保持されるという事実を利用できます。型トークンは、ジェネリック型情報を便利に格納する小さな匿名クラスです。

今あなたのコードに:

Type responseType = new TypeToken<Response<T>>() {}.getType();

クラスのこの行では、コンパイル時にハード コードされた (バインドされた) 型を持つExecutor匿名クラス (から継承) を作成します。したがって、実行時に、GSON は のオブジェクトが必要であると判断できます。しかし、コンパイル時に指定しなかったため、何が何であるかはわかりません! そのため、GSON は作成するオブジェクトの型を判断できず、代わりにを作成するだけです。TypeTokenResponse<T>Response<T>TListResponseStringMap

Tこの話の教訓は、コンパイル時にそれを指定する必要があるということです。が一般的に使用されることを意図している場合Executorは、おそらくクライアント コードで、そのクラスの外部でタイプ トークンを作成する必要があります。何かのようなもの:

class Executor<T> {

    private TypeToken<Response<T>> responseType;
    private Response<T> response;

    public Executor(TypeToken<Response<T>> responseType) {
        this.responseType = responseType;
    }

    public void execute() {
        this.response = new Gson().fromJson(json, responseType.getType());
    }

    public Response<T> getResponse() { return this.response; }

}

// client code:
Executor<Language> executor = new Executor<Language>(new TypeToken<Response<Language>>() { });
executor.execute();
List<Language> languages = executor.getResponse().getData();
System.out.println(languages.get(0).alias); // prints "be"

ちなみに、私は自分のマシンで上記をテストしました。

長すぎてすみません!

于 2013-01-24T16:33:24.843 に答える
2

この声明のresponse.execute();後、あなたは電話していません。Executor<Language> executor = new Executor<Language>();ここでは Java ジェネリックを利用することはできませんが、次のコードで同じ効果を得ることができます。

Response.java

import java.io.Serializable;
import java.util.List;

/**
 *
 * @author visruth
 */
public class Response<T> implements Serializable {

    private List<T> data = null;

    public List<T> getData() {
        return this.data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

}

言語.java

import java.io.Serializable;

/**
 *
 * @author visruth
 */
public class Language implements Serializable {
    private String alias;
    private String label;

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

}

最後に、Executor.java

import com.google.gson.Gson;
import java.util.*;

/**
 *
 * @author visruth
 */
public class Executor<T> {
private Response<T> response;

    public Response<T> getResponse() {
        return response;
    }

    /**
     * @param args the command line arguments
     */
    public void executor() {
        //sample data
        Response<Language> response=new Response<Language>();
        Language lan1=new Language();
        lan1.setAlias("alias1");
        lan1.setLabel("label1");
        Language lan2=new Language();
        lan2.setAlias("alias2");
        lan2.setLabel("label2");
        List<Language> listOfLangauges=new ArrayList<Language>();
        listOfLangauges.add(lan1);
        listOfLangauges.add(lan2);
        response.setData(listOfLangauges);
        Gson gson=new Gson();
        String json = gson.toJson(response);

        System.out.println(json);
        Response<Language> jsonResponse = gson.fromJson(json, Response.class);
        List list=jsonResponse.getData();

        List<Language> langs=new ArrayList<Language>();            
        for(int i=0; i<list.size(); i++) {            
        Language lan=gson.fromJson(list.get(i).toString(), Language.class);
        langs.add(lan);
        //System.out.println(lan.getAlias());
        }
        Response<Language> responseMade=new Response<Language>();
        responseMade.setData(langs);
        this.response=(Response<T>) responseMade;

    }
}

次のようにテストできます

Executor<Language> executor = new Executor<Language>();
executor.executor();
List<Language> data = executor.getResponse().getData();
for(Language langu: data) {
    System.out.println(langu.getAlias());
    System.out.println(langu.getLabel());
}
于 2013-01-24T16:36:58.590 に答える
0

あなたの問題はJavaタイプの消去にリンクされています。ジェネリックはコンパイル時にのみ認識されます。

悲しいことに、リフレクションを使用して、どのクラスにデシリアライズする必要があるかを GSON が知る方法はありません。

于 2013-01-24T16:22:01.263 に答える