• GSON使用笔记(3) -- 如何反序列化出List


    GSON使用笔记(3) -- 如何反序列化出List

    本文通过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帮忙:

    @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 这个类来帮助我们捕获(capture)像ArrayList<MyObj>这样的泛型信息。test3()的第一行代码创建了一个 匿名内部类 ,这样,Java编译器就会把泛型信息编译到这个匿名内部类里,然后在运行时就可以被 getType() 方法用 反射API 提取到。

    问题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类型的对象。

  • 相关阅读:
    解决springmvc报错,java.lang.IllegalArgumentException:No converter found for return value of type: class .......
    BidiMap MultiMap LazyMap
    双色球2013年统计,从网上爬虫出来的
    捡石子小游戏程序解析
    Linux常用命令--List of commands(附目录切换命令)
    格式化字符串
    有关循环
    使用python 3.x 对pythonchallenge-----8的解答过程
    使用python 3.x 对pythonchallenge-----7的解答过程
    使用python 3.x 对pythonchallenge-----6的解答过程
  • 原文地址:https://www.cnblogs.com/u0mo5/p/4361719.html
Copyright © 2020-2023  润新知