分享

GSON使用笔记(3)

 王庆峰 2015-06-19

本文通过3个问题来讨论如何使用GSON把JSON反序列化为List。

问题1

有这样两个类:

  1. class MyObj {  
  2.     int x;  
  3. }  
  4.   
  5. class MyList {  
  6.     List<MyObj> objList = new LinkedList<>();  
  7. }  
那下面的测试能通过吗?
  1. @Test  
  2. public void test1() {  
  3.     MyList myList = new Gson().fromJson("{objList:[]}", MyList.class);  
  4.     Assert.assertEquals(LinkedList.class, myList.objList.getClass());  
  5. }  

答案1

答案是,测试通不过!原因是GSON不知道objList的具体类型,因此只能选择默认的ArrayList。更详细的解释,可以参考这篇文章和ConstructorConstructor类的源代码。如果确实想让GSON创建LinkedList实例该怎么办呢?也简单,就是给objList一个更具体的类型:

  1. class MyList {  
  2.     LinkedList<MyObj> objList = new LinkedList<>();  
  3. }  

问题2

下面的测试能通过吗?

  1. @Test  
  2. public void test2() {  
  3.     ArrayList<?> list = new Gson().fromJson("[{x:1}]", ArrayList.class);  
  4.     Assert.assertEquals(1, list.size());  
  5.     Assert.assertEquals(MyObj.class, list.get(0).getClass());  
  6. }  

答案2

很明显,不能。因为fromJson方法不能从"[{x:1}]"参数推测出数组里放的是MyObj类型的对象,也无法从ArrayList.class参数得到这个信息。那么改成下面这样呢?

  1. ArrayList<MyObj> list = new Gson().fromJson("[{x:1}]", ArrayList<MyObj>.class);  
更糟糕,连编译都无法通过!因为Java的泛型是用擦拭法实现的,说白了只是编译器提供给程序员的语法糖,根本不存在ArrayList<MyObj>这样一个类。那么怎样才能让GSON反序列化出我们想要的泛型对象呢?答案是,请TypeToken帮忙:
  1. @Test  
  2. public void test3() {  
  3.     Type type = new TypeToken<ArrayList<MyObj>>() {}.getType();  
  4.     ArrayList<MyObj> list = new Gson().fromJson("[{x:1}]", type);  
  5.     Assert.assertEquals(1, list.size());  
  6.     Assert.assertEquals(MyObj.class, list.get(0).getClass());  
  7. }  
GSON提供了TypeToken这个类来帮助我们捕获(capture)像ArrayList<MyObj>这样的泛型信息。test3()的第一行代码创建了一个匿名内部类,这样,Java编译器就会把泛型信息编译到这个匿名内部类里,然后在运行时就可以被getType()方法用反射API提取到。

问题3

如果我想写一个通用的方法,把json反序列化成List,下面这个方法可行吗?

  1. public static <T> ArrayList<T> jsonToList(String json, Class<T> classOfT) {  
  2.     Type type = new TypeToken<ArrayList<T>>() {}.getType();  
  3.     return new Gson().fromJson(json, type);  
  4. }  
这个测试能通过吗?
  1. @Test  
  2. public void test4() {  
  3.     ArrayList<MyObj> list = jsonToList("[{x:1}]", MyObj.class);  
  4.     Assert.assertEquals(1, list.size());  
  5.     Assert.assertEquals(MyObj.class, list.get(0).getClass());  
  6. }  

答案3

答案是,方法不可行(虽然能编译通过),测试通不过!还是因为Java泛型的擦除法,详细的回答可以看stackoverflow上的这个问题。那还有办法实现jsonToList()这个通用方法呢?有的,只是稍微复杂一点:

  1. public static <T> ArrayList<T> jsonToList(String json, Class<T> classOfT) {  
  2.     Type type = new TypeToken<ArrayList<JsonObject>>(){}.getType();  
  3.     ArrayList<JsonObject> jsonObjs = new Gson().fromJson(json, type);  
  4.       
  5.     ArrayList<T> listOfT = new ArrayList<>();  
  6.     for (JsonObject jsonObj : jsonObjs) {  
  7.         listOfT.add(new Gson().fromJson(jsonObj, classOfT));  
  8.     }  
  9.       
  10.     return listOfT;  
  11. }  
分两步,先反序列化出ArrayList<JsonObject>,然后在一个个的把JsonObject转成classOfT类型的对象。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多