本文通過(guò)3個(gè)問(wèn)題來(lái)討論如何使用GSON把JSON反序列化為L(zhǎng)ist。
問(wèn)題1
有這樣兩個(gè)類(lèi):
- class MyObj {
- int x;
- }
-
- class MyList {
- List<MyObj> objList = new LinkedList<>();
- }
那下面的測(cè)試能通過(guò)嗎?
- @Test
- public void test1() {
- MyList myList = new Gson().fromJson("{objList:[]}", MyList.class);
- Assert.assertEquals(LinkedList.class, myList.objList.getClass());
- }
答案1
答案是,測(cè)試通不過(guò)!原因是GSON不知道objList的具體類(lèi)型,因此只能選擇默認(rèn)的ArrayList。更詳細(xì)的解釋?zhuān)梢詤⒖?a target="_blank" >這篇文章和ConstructorConstructor類(lèi)的源代碼。如果確實(shí)想讓GSON創(chuàng)建LinkedList實(shí)例該怎么辦呢?也簡(jiǎn)單,就是給objList一個(gè)更具體的類(lèi)型:
- class MyList {
- LinkedList<MyObj> objList = new LinkedList<>();
- }
問(wèn)題2
下面的測(cè)試能通過(guò)嗎?
- @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
很明顯,不能。因?yàn)閒romJson方法不能從"[{x:1}]"參數(shù)推測(cè)出數(shù)組里放的是MyObj類(lèi)型的對(duì)象,也無(wú)法從ArrayList.class參數(shù)得到這個(gè)信息。那么改成下面這樣呢?
- ArrayList<MyObj> list = new Gson().fromJson("[{x:1}]", ArrayList<MyObj>.class);
更糟糕,連編譯都無(wú)法通過(guò)!因?yàn)镴ava的泛型是用擦拭法實(shí)現(xiàn)的,說(shuō)白了只是編譯器提供給程序員的語(yǔ)法糖,根本不存在ArrayList<MyObj>這樣一個(gè)類(lèi)。那么怎樣才能讓GSON反序列化出我們想要的泛型對(duì)象呢?答案是,請(qǐng)TypeToken幫忙:
- @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());
- }
GSON提供了TypeToken這個(gè)類(lèi)來(lái)幫助我們捕獲(capture)像ArrayList<MyObj>這樣的泛型信息。test3()的第一行代碼創(chuàng)建了一個(gè)匿名內(nèi)部類(lèi),這樣,Java編譯器就會(huì)把泛型信息編譯到這個(gè)匿名內(nèi)部類(lèi)里,然后在運(yùn)行時(shí)就可以被getType()方法用反射API提取到。
問(wèn)題3
如果我想寫(xiě)一個(gè)通用的方法,把json反序列化成List,下面這個(gè)方法可行嗎?
- public static <T> ArrayList<T> jsonToList(String json, Class<T> classOfT) {
- Type type = new TypeToken<ArrayList<T>>() {}.getType();
- return new Gson().fromJson(json, type);
- }
這個(gè)測(cè)試能通過(guò)嗎?
- @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
答案是,方法不可行(雖然能編譯通過(guò)),測(cè)試通不過(guò)!還是因?yàn)镴ava泛型的擦除法,詳細(xì)的回答可以看stackoverflow上的這個(gè)問(wèn)題。那還有辦法實(shí)現(xiàn)jsonToList()這個(gè)通用方法呢?有的,只是稍微復(fù)雜一點(diǎn):
- 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>,然后在一個(gè)個(gè)的把JsonObject轉(zhuǎn)成classOfT類(lèi)型的對(duì)象。
|