Gsonが汎用オブジェクトを解析する場合のTypeTokenの使い方

4883 ワード

Android開発では,インタフェースサーバからデータを取得し,携帯電話インタフェースに表示することがしばしば必要である.ここで、携帯電話端末とインタフェースサーバとの間では、通常、jsonデータを用いて通信が行われる.
よく使用される解析シーンは次のとおりです.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{
" + " \"name\": \"BeJson\"}"; Gson gson = new Gson(); DataBean bean = gson.fromJson(jsonData, DataBean.class); Log.e("MainActivity", "bean name: " + bean.name); Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean)); } class DataBean{ public String name; } }

しかし、上記のコードは汎用性に遭遇すると問題が発生します.例は次のとおりです.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{
" + " \"name\": \"BeJson\"}"; Gson gson = new Gson(); DataBean bean = gson.fromJson(jsonData, DataBean.class); Log.e("MainActivity", "bean name: " + bean.name); Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean)); Foo foo = new Foo(); foo.value = bean; // gson 2.2.4 , Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo)); String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}"; Foo genericBean = gson.fromJson(genericData, foo.getClass()); // ,genericBean.value com.google.gson.internal.LinkedTreeMap。 Log.e("MainActivity", "generic bean value : " + genericBean.value.toString()); } class DataBean{ public String name; } class Foo { T value; } }

上記の問題の原因は、Javaが実行中に汎用パラメータのタイプが消去され、実行中にすべての汎用タイプがObjectタイプになるためです.
汎用ランタイムタイプ消去
汎用的なランタイムタイプ消去の問題は、以下の例で説明します.
//  
ArrayList stringList = Lists.newArrayList();
ArrayList intList = Lists.newArrayList();
System.out.println("intList type is " + intList.getClass());
System.out.println("stringList type is " + stringList.getClass());
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));

//    
intList type is class java.util.ArrayList
stringList type is class java.util.ArrayList
true

上記のコードの2つの汎用タイプのArrayListは、1つのパラメータがStringであり、もう1つはIntegerであり、getClassメソッドのタイプを出力するとjava.util.ArrayListである.一方、汎用タイプ消去により、3行目の出力がtrueになり、stringListとintListタイプが一致すると考えられます.
TypeTokenクラスはjava実行時に汎用タイプが消去される問題を解決するために使用されます.TypeTokenにより特定のパラメータタイプを得ることができる.次の例の出力結果がわかります.
TypeToken> typeToken = new TypeToken>() {};
System.out.println(typeToken.getType());

//    
java.util.ArrayList

TypeTokenの具体的な原理については、参考文書Android:GsonがTypeTokenを利用して汎用パラメータのタイプを取得する方法を参照してください.
ソリューション
上記の実行エラーのコードに対して、TypeTokenを使用して問題を修正することができます.例は次のとおりです.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{
" + " \"name\": \"BeJson\"}"; Gson gson = new Gson(); DataBean bean = gson.fromJson(jsonData, DataBean.class); Log.e("MainActivity", "bean name: " + bean.name); Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean)); Foo foo = new Foo(); foo.value = bean; Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo)); String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}"; TypeToken> typeToken = new TypeToken>(){}; Foo genericBean = gson.fromJson(genericData, typeToken.getType()); Log.e("MainActivity", "generic bean value : " + gson.toJson(genericBean.value)); } class DataBean{ public String name; } class Foo { T value; } }

参照ドキュメント:guava反射TypeToken汎用ランタイムタイプ消去の問題解決Android:Gson汎用パラメータのタイプをTypeTokenで取得する方法Android StudioにGSONを追加し、GsonFormatを使用してエンティティクラスを迅速に実現する方法