本文通過3個問題來討論如何使用GSON把JSON反序列化為List。
有這樣兩個類:
class MyObj { int x; } class MyList { List那下面的測試能通過嗎?objList = new LinkedList<>(); }
@Test public void test1() { MyList myList = new Gson().fromJson("{objList:[]}", MyList.class); Assert.assertEquals(LinkedList.class, myList.objList.getClass()); }
答案是,測試通不過!原因是GSON不知道objList的具體類型,因此只能選擇默認的ArrayList。更詳細的解釋,可以參考這篇文章和ConstructorConstructor類的源代碼。如果確實想讓GSON創建LinkedList實例該怎麼辦呢?也簡單,就是給objList一個更具體的類型:
class MyList { LinkedListobjList = new LinkedList<>(); }
下面的測試能通過嗎?
@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()); }
很明顯,不能。因為fromJson方法不能從"[{x:1}]"參數推測出數組裡放的是MyObj類型的對象,也無法從ArrayList.class參數得到這個信息。那麼改成下面這樣呢?
ArrayList更糟糕,連編譯都無法通過!因為Java的泛型是用擦拭法實現的,說白了只是編譯器提供給程序員的語法糖,根本不存在ArrayListlist = new Gson().fromJson("[{x:1}]", ArrayList .class);
@Test public void test3() { Type type = new TypeToken>() {}.getType(); ArrayListGSON提供了TypeToken這個類來幫助我們捕獲(capture)像ArrayListlist = new Gson().fromJson("[{x:1}]", type); Assert.assertEquals(1, list.size()); Assert.assertEquals(MyObj.class, list.get(0).getClass()); }
如果我想寫一個通用的方法,把json反序列化成List,下面這個方法可行嗎?
public static這個測試能通過嗎?ArrayList jsonToList(String json, Class classOfT) { Type type = new TypeToken>() {}.getType(); return new Gson().fromJson(json, type); }
@Test public void test4() { ArrayListlist = jsonToList("[{x:1}]", MyObj.class); Assert.assertEquals(1, list.size()); Assert.assertEquals(MyObj.class, list.get(0).getClass()); }
答案是,方法不可行(雖然能編譯通過),測試通不過!還是因為Java泛型的擦除法,詳細的回答可以看stackoverflow上的這個問題。那還有辦法實現jsonToList()這個通用方法呢?有的,只是稍微復雜一點:
public static分兩步,先反序列化出ArrayListArrayList jsonToList(String json, Class classOfT) { Type type = new TypeToken>(){}.getType(); ArrayList jsonObjs = new Gson().fromJson(json, type); ArrayList listOfT = new ArrayList<>(); for (JsonObject jsonObj : jsonObjs) { listOfT.add(new Gson().fromJson(jsonObj, classOfT)); } return listOfT; }