GSON使用筆記(3) -- 如何反序列化出List
阿新 • • 發佈:2018-12-26
本文通過3個問題來討論如何使用GSON把JSON反序列化為List。
問題1
有這樣兩個類:
class MyObj {
int x;
}
class MyList {
List<MyObj> objList = new LinkedList<>();
}
那下面的測試能通過嗎?
@Test public void test1() { MyList myList = new Gson().fromJson("{objList:[]}", MyList.class); Assert.assertEquals(LinkedList.class, myList.objList.getClass()); }
答案1
答案是,測試通不過!原因是GSON不知道objList的具體型別,因此只能選擇預設的ArrayList。更詳細的解釋,可以參考這篇文章和ConstructorConstructor類的原始碼。如果確實想讓GSON建立LinkedList例項該怎麼辦呢?也簡單,就是給objList一個更具體的型別:
class MyList {
LinkedList<MyObj> objList = new LinkedList<>();
}
問題2
下面的測試能通過嗎?
@Test public void test2() { ArrayList<?> list = new Gson().fromJson("[{x:1}]", ArrayList.class); Assert.assertEquals(1, list.size()); Assert.assertEquals(MyObj.class, list.get(0).getClass()); }
答案2
很明顯,不能。因為fromJson方法不能從"[{x:1}]"引數推測出數組裡放的是MyObj型別的物件,也無法從ArrayList.class引數得到這個資訊。那麼改成下面這樣呢?
ArrayList<MyObj> list = new Gson().fromJson("[{x:1}]", ArrayList<MyObj>.class);
更糟糕,連編譯都無法通過!因為Java的泛型是用擦拭法實現的,說白了只是編譯器提供給程式設計師的語法糖,根本不存在ArrayList<MyObj>這樣一個類。那麼怎樣才能讓GSON反序列化出我們想要的泛型物件呢?答案是,請TypeToken幫忙:
GSON提供了TypeToken這個類來幫助我們捕獲(capture)像ArrayList<MyObj>這樣的泛型資訊。test3()的第一行程式碼建立了一個匿名內部類,這樣,Java編譯器就會把泛型資訊編譯到這個匿名內部類裡,然後在執行時就可以被getType()方法用反射API提取到。@Test public void test3() { Type type = new TypeToken<ArrayList<MyObj>>() {}.getType(); ArrayList<MyObj> list = new Gson().fromJson("[{x:1}]", type); Assert.assertEquals(1, list.size()); Assert.assertEquals(MyObj.class, list.get(0).getClass()); }
問題3
如果我想寫一個通用的方法,把json反序列化成List,下面這個方法可行嗎?
public static <T> ArrayList<T> jsonToList(String json, Class<T> classOfT) {
Type type = new TypeToken<ArrayList<T>>() {}.getType();
return new Gson().fromJson(json, type);
}
這個測試能通過嗎?
@Test
public void test4() {
ArrayList<MyObj> list = jsonToList("[{x:1}]", MyObj.class);
Assert.assertEquals(1, list.size());
Assert.assertEquals(MyObj.class, list.get(0).getClass());
}
答案3
答案是,方法不可行(雖然能編譯通過),測試通不過!還是因為Java泛型的擦除法,詳細的回答可以看stackoverflow上的這個問題。那還有辦法實現jsonToList()這個通用方法呢?有的,只是稍微複雜一點:
public static <T> ArrayList<T> jsonToList(String json, Class<T> classOfT) {
Type type = new TypeToken<ArrayList<JsonObject>>(){}.getType();
ArrayList<JsonObject> jsonObjs = new Gson().fromJson(json, type);
ArrayList<T> listOfT = new ArrayList<>();
for (JsonObject jsonObj : jsonObjs) {
listOfT.add(new Gson().fromJson(jsonObj, classOfT));
}
return listOfT;
}
分兩步,先反序列化出ArrayList<JsonObject>,然後在一個個的把JsonObject轉成classOfT型別的物件。