GsonでJSON文字列をGenericsなオブジェクトに変換するときにハマる
GsonのGはガンダムの「G」です(違
JavaでJSONを扱うときにいつもお世話になっております。
で、タイトル通り、ハマったのでメモ。
変換対象のクラス
こんな感じで共通部以外をジェネリクスでやっちゃうぜ的なイメージ。
class Hoge<T> { String name; int id; T value; @Override public String toString() { return "Hoge{" + "name='" + name + '\'' + ", id=" + id + ", value=" + value + '}'; } } class Fuga { String name; int id; public Fuga(String name, int id) { this.name = name; this.id = id; } @Override public String toString() { return "Fuga{" + "name='" + name + '\'' + ", id=" + id + '}'; } }
GenericsなオブジェクトからJSONへの変換
JSONへの変換はこんな感じで何も考えずに行ける
import com.google.gson.Gson; public class GsonTest { public static void main( String ... args){ Hoge<Fuga> hoge = new Hoge<Fuga>(); hoge.name = "aaaa"; hoge.id = 3; Fuga fuga = new Fuga("bbbb",1000); hoge.value = fuga; System.out.println(String.format("Object->[%s]", hoge)); } }
実行するとこうなる。
Object->[Hoge{name='aaaa', id=3, value=Fuga{name='bbbb', id=1000}}] JSON->[{"name":"aaaa","id":3,"value":{"name":"bbbb","id":1000}}]
JSONからオブジェクトへの変換
「Gson User Guide」にやり方が書いてある。
親切だ。
サンプルどおりの手順
import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public class GsonTest { public static void main( String ... args){ // 上で作ったjsonをオブジェクトに変換 hoge = decode(json); System.out.println(String.format("Object(Hoge)->[%s]", hoge)); fuga = hoge.value; System.out.println(String.format("Object(Fuga)->[%s]", fuga)); } private static Hoge<Fuga> decode( String json ){ Gson gson = new Gson(); Type type = new TypeToken<Hoge<Fuga>>(){}.getType(); System.out.println(type.getTypeName()); return gson.fromJson(json, type); } }
実行するとこうなる。
Main$Hoge<Main$Fuga> Object(Hoge)->[Hoge{name='aaaa', id=3, value=Fuga{name='bbbb', id=1000}}] Object(Fuga)->[Fuga{name='bbbb', id=1000}]
汎用化してみる
実際はHoge
import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public class GsonTest { public static void main( String ... args){ // 上で作ったjsonをオブジェクトに変換 hoge = decode(json, Fuga.class); System.out.println(String.format("Object(Hoge)->[%s]", hoge)); fuga = hoge.value; System.out.println(String.format("Object(Fuga)->[%s]", fuga)); } private static <T> Hoge<T> decode( String json, Class<T> tClass ) { Gson gson = new Gson(); Type type = new TypeToken<Hoge<T>>(){}.getType(); System.out.println(type.getTypeName()); return gson.fromJson(json, type); } }
実行するとこうなる。
Main$Hoge<T> Object(Hoge)->[Hoge{name='aaaa', id=3, value={name=bbbb, id=1000.0}}] Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to Main$Fuga at Main.main(Main.java:xx)
GsonのfromJsonの結果はHogeクラスまではデコードできているんだけど、valueがFugaクラスではなくて「com.google.gson.internal.LinkedTreeMap」になっている。
困った…
ということで、StackOverFlowさん出番です。
Once your .java source file is compiled, the type parameter
is obviously thrown away. Since it is not known at compile time, it cannot be stored in bytecode, so it's erased and Gson can't read it.
要するに
"<T>"はビルド時に消えるので動的には見えねよバーカバーカ。だから親切心でLinkedTreeMapにしといてやったわー。
ってことらしい。
ということで上記の回答にあったような、ParameterizedTypeを実装したジェネリクスの情報を保持するクラスを作ればよい。
class GenericOf<X, Y> implements ParameterizedType { private final Class<X> container; private final Class<Y> wrapped; public GenericOf(Class<X> container, Class<Y> wrapped) { this.container = container; this.wrapped = wrapped; } @Override public Type[] getActualTypeArguments() { return new Type[]{wrapped}; } @Override public Type getRawType() { return container; } @Override public Type getOwnerType() { return null; } }
import com.google.gson.Gson; import java.lang.reflect.ParameterizedType; public class GsonTest { public static void main( String ... args){ // 上で作ったjsonをオブジェクトに変換 hoge = decode(json, Fuga.class); System.out.println(String.format("Object(Hoge)->[%s]", hoge)); fuga = hoge.value; System.out.println(String.format("Object(Fuga)->[%s]", fuga)); } private static <T> Hoge<T> decode( String json, Class<T> tClass ) { Gson gson = new Gson(); ParameterizedType type = new GenericOf(Hoge.class,tClass); System.out.println(type.getRawType()); System.out.println(type.getActualTypeArguments()[0]); return gson.fromJson(json, type); } }
実行するとこうなる。
class Main$Hoge class Main$Fuga Object(Hoge)->[Hoge{name='aaaa', id=3, value=Fuga{name='bbbb', id=1000}}] Object(Fuga)->[Fuga{name='bbbb', id=1000}]
- 作者: 小畑健
- 出版社/メーカー: 集英社
- 発売日: 2011/08/18
- メディア: 文庫
- クリック: 7回
- この商品を含むブログを見る
Rubie'sルービーズ 大人用ジェーソン ハロウィン コスプレ 衣装 ディズニー
- 出版社/メーカー: RUBIE'S JAPAN
- メディア:
- この商品を含むブログを見る
- 出版社/メーカー: NECA
- メディア:
- この商品を含むブログを見る