Gson是Google开发来用来序列化和反序列化json格式数据的java库,他最大的特点就是对复杂类型的支持度高,可以完美解决java泛型问题,这得益于他对泛型类型数据的特殊处理,他的缺点就是速度慢。

  我们首先看下例子:

  1. List<MerchantMessage> merchants = new ArrayList<MerchantMessage>();
  2. MerchantMessage m1 = new MerchantMessage();
  3. List<Photo> p1 = new ArrayList<Photo>();
  4. p1.add(new Photo("zcm", "http://zcm.jpg"));
  5. m1.setPhotos(p1);
  6. m1.setAdcode("123");
  7. m1.setAddress(123123);
  8. m1.setBusinessArea("hello");
  9. merchants.add(m1);
  10. ResponseMerchantResult result = new ResponseMerchantResult(0, merchants);
  11. String json = Gson.toJson(result);
         Gson.formJson(json,ResponseMerchantResult.class);

  我们构建了一个ResponseMerchantResult类型的数据,然后执行Gson.toJson方法,然后就可以得到相应的json字符串,我们来看下解析过程

  最终Gson会执行到这个方法:

  1. @SuppressWarnings("unchecked")
  2. public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
  3. TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
  4. boolean oldLenient = writer.isLenient();
  5. writer.setLenient(true);
  6. boolean oldHtmlSafe = writer.isHtmlSafe();
  7. writer.setHtmlSafe(htmlSafe);
  8. boolean oldSerializeNulls = writer.getSerializeNulls();
  9. writer.setSerializeNulls(serializeNulls);
  10. try {
  11. ((TypeAdapter<Object>) adapter).write(writer, src);
  12. } catch (IOException e) {
  13. throw new JsonIOException(e);
  14. } finally {
  15. writer.setLenient(oldLenient);
  16. writer.setHtmlSafe(oldHtmlSafe);
  17. writer.setSerializeNulls(oldSerializeNulls);
  18. }
  19. }

我们来分析下:  

  1. getAdapter(TypeToken.get(typeOfSrc));
  2.  
  3. 这个代码是获取到解析解析的适配器,根据typeOfSrc匹配现有的适配器,以后所有的操作就是这个适配器来完成。
  1. @SuppressWarnings("unchecked")
  2. public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
  3. TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
  4. if (cached != null) {
  5. return (TypeAdapter<T>) cached;
  6. }
  7.  
  8. Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
  9. boolean requiresThreadLocalCleanup = false;
  10. if (threadCalls == null) {
  11. threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
  12. calls.set(threadCalls);
  13. requiresThreadLocalCleanup = true;
  14. }
  15.  
  16. // the key and value type parameters always agree
  17. FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
  18. if (ongoingCall != null) {
  19. return ongoingCall;
  20. }
  21.  
  22. try {
  23. FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
  24. threadCalls.put(type, call);
  25.  
  26. for (TypeAdapterFactory factory : factories) {
  27. TypeAdapter<T> candidate = factory.create(this, type);
  28. if (candidate != null) {
  29. call.setDelegate(candidate);
  30. typeTokenCache.put(type, candidate);
  31. return candidate;
  32. }
  33. }
  34. throw new IllegalArgumentException("GSON cannot handle " + type);
  35. } finally {
  36. threadCalls.remove(type);
  37.  
  38. if (requiresThreadLocalCleanup) {
  39. calls.remove();
  40. }
  41. }
  42. }

我们看到这个方法会遍历所有的适配器工厂,然后得到一个合适的,我们对Gson扩展时最大的扩展点就是这个地方,你可以替换他本身的适配器工厂,也可以添加你需要的工厂。

我们来看下最常用的一个适配器工厂ReflectiveTypeAdapterFactory  --- 反射类型适配器工厂类

  

  1. @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
  2. Class<? super T> raw = type.getRawType();
  3.  
  4. if (!Object.class.isAssignableFrom(raw)) {
  5. return null; // it's a primitive!
  6. }
  7.  
  8. ObjectConstructor<T> constructor = constructorConstructor.get(type);
  9. return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
  10. }
  1. 最关键的就是这个create方法,这个方法最终会创建一个需要的适配器,这个适配器工厂会放在所有工厂的最后,当我们要解析的类不是基本类或常用的java类时,就会使用这个工厂来解析
    我们看到getBoundFields()方法
  1. private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
  2. Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
  3. if (raw.isInterface()) {
  4. return result;
  5. }
  6.  
  7. Type declaredType = type.getType();
  8. while (raw != Object.class) {
  9. Field[] fields = raw.getDeclaredFields();
  10. for (Field field : fields) {
  11. boolean serialize = excludeField(field, true);
  12. boolean deserialize = excludeField(field, false);
  13. if (!serialize && !deserialize) {
  14. continue;
  15. }
  16. field.setAccessible(true);
  17. Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
  18. List<String> fieldNames = getFieldNames(field);
  19. BoundField previous = null;
  20. for (int i = 0, size = fieldNames.size(); i < size; ++i) {
  21. String name = fieldNames.get(i);
  22. if (i != 0) serialize = false; // only serialize the default name
  23. BoundField boundField = createBoundField(context, field, name,
  24. TypeToken.get(fieldType), serialize, deserialize);
  25. BoundField replaced = result.put(name, boundField);
  26. if (previous == null) previous = replaced;
  27. }
  28. if (previous != null) {
  29. throw new IllegalArgumentException(declaredType
  30. + " declares multiple JSON fields named " + previous.name);
  31. }
  32. }
  33. type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
  34. raw = type.getRawType();
  35. }
  36. return result;
  37. }

看到这里我们就会发现会获取所有的字段,然后为每个字段绑定他对应的适配器,这样被解析类的每个属性都有了相应的解析适配器,接下来就可以进行解析了

  1. public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
  2. TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
  3. boolean oldLenient = writer.isLenient();
  4. writer.setLenient(true);
  5. boolean oldHtmlSafe = writer.isHtmlSafe();
  6. writer.setHtmlSafe(htmlSafe);
  7. boolean oldSerializeNulls = writer.getSerializeNulls();
  8. writer.setSerializeNulls(serializeNulls);
  9. try {
  10. ((TypeAdapter<Object>) adapter).write(writer, src);
  11. } catch (IOException e) {
  12. throw new JsonIOException(e);
  13. } finally {
  14. writer.setLenient(oldLenient);
  15. writer.setHtmlSafe(oldHtmlSafe);
  16. writer.setSerializeNulls(oldSerializeNulls);
  17. }
  18. }

我们看到这里会调用适配器的write方法:

  1. public void write(JsonWriter out, T value) throws IOException {
  2. if (value == null) {
  3. out.nullValue();
  4. return;
  5. }
  6.  
  7. out.beginObject();
  8. try {
  9. for (BoundField boundField : boundFields.values()) {
  10. if (boundField.writeField(value)) {
  11. out.name(boundField.name);
  12. boundField.write(out, value);
  13. }
  14. }
  15. } catch (IllegalAccessException e) {
  16. throw new AssertionError(e);
  17. }
  18. out.endObject();
  19. }
  20. }

这里会循环所有的属性,然后调用对应的适配器的write方法,最终序列化就完成了。

我觉得Gson最好的地方就是他可以自动适配泛型类型,我们来看个类

  1. public class TypeToken<T> {
  2. final Class<? super T> rawType;
  3. final Type type;
  4. final int hashCode;
  5.  
  6. @SuppressWarnings("unchecked")
  7. TypeToken(Type type) {
  8. this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
  9. this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
  10. this.hashCode = this.type.hashCode();
  11. }
  12. }

这个类是Gson用来记录解析过程需要的type类型,当寻找适配器时就是根据这个类来寻找符合的适配器的,所以泛型的配置也是在这个类里配置。

我们来看下怎么配置这个类

  首先看几个基本类

  1. public class GenericResult<T>{
  2.  
  3. private long l1;
  4.  
  5. private double d1;
  6.  
  7. private int i1;
  8.  
  9. private String s1;
  10.  
  11. private List<T> datas;
  12.  
  13. }
  1. public class Message<T> {
  2.  
  3. private List<T> data;
  4.  
  5. public List<T> getData() {
  6. return data;
  7. }
  8.  
  9. public void setData(List<T> data) {
  10. this.data = data;
  11. }
  12.  
  13. }
  1. GenericResult<Message> result2 = new GenericResult<Message>();
  2. Message<MerchantMessage> message = new Message();
  3. message.setData(merchants);
  4. result2.setD1(11d);
  5. result2.setI1(1);
  6. result2.setL1(11l);
  7. result2.setS1("11");
  8. result2.setDatas(Arrays.asList(message));
  9. String json2 = JsonTool.toJson(result2);

我们看到最后这段代码,这段代码主要是构建一个java类,其中主要类是GenericResult,然后他包含泛型类型Message,Message包含泛型类型MerchantMessage

那我们解析的时候怎么解析呢

我们需要一个辅助类

  下面或者类就可以记录我们所有的类型,我们看下怎么使用

  1. public class TypeMessage implements ParameterizedType{
  2.  
  3. private final Class main;
  4.  
  5. private final Type[] args;
  6.  
  7. public TypeMessage(Class main,Type[] args){
  8. this.main = main;
  9. this.args = args == null ? new Type[0] : args;
  10. }
  11.  
  12. @Override
  13. public Type[] getActualTypeArguments() {
  14. return this.args;
  15. }
  16.  
  17. @Override
  18. public Type getRawType() {
  19. return this.main;
  20. }
  21.  
  22. @Override
  23. public Type getOwnerType() {
  24. return null;
  25. }
  26.  
  27. }
  1. JsonTool.<GenericResult>toJson(json2,
  2. new TypeMessage(
  3. GenericResult.class,
  4. new Type[]{new TypeMessage(Message.class,new Type[]{MerchantMessage.class})}))

当Gson去解析的时候就会按照我们配置的类型去解析。这样我们的泛型就被我们放置进去了。

以上就是toJson的解析,formJson的解析和toJson类似这里就不再看了。

  1.  

Gson学习记录的更多相关文章

  1. kotlin电商学习记录,好久没来逛逛了

    好久没来,一直做毕业设计,用kotlin写一个基于以图搜图的购物app,现在又赶上实习,内容多,时间少,不过前途光明并由贵人指点.加油 kotlin电商学习记录 技术选型 视图层 kotlin-and ...

  2. Android开发技术周报183学习记录

    Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...

  3. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  4. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  5. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  6. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  7. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  8. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

  9. UWP学习记录9-设计和UI之控件和模式6

    UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...

随机推荐

  1. 继承 多态 java相关基础知识

    1:静态语句块.构造语句块(就是只有大括号的那块)以及构造函数的执行顺序 例子: class HelloA { public HelloA() { System.out.println("H ...

  2. 实训三(cocos2dx 3.x 打包apk)

    上一篇文章<实训二(cocos2dx 2.x 打包apk)>简单的讲述的利用cocos2dx 2.x引擎在windows平台上打包apk的方法与过程,本文将介绍3.x版本引擎,如何打包ap ...

  3. time since epoch

    C++11 提供了新的获取系统时间的库函数,在获取时间的时候一般常用的是获取time since epoch,下面来看一下如何获取这个时间. #include <iostream> #in ...

  4. ORACLE创建数据库时无法创建目录

    ORACLE创建数据库时无法创建目录,如图所示信息 原因:没有创建写入的权限 解决:修改文件夹权限即可 F:\oracle\product\10.2.0\db_1\cfgtoollogs\dbca 增 ...

  5. WebView 5.0+闪烁以及白屏问题完美解决

    Android webView 在5.0+上启动硬件加速,造成部分手机出现闪烁.白屏等现象 必须写下这篇博客,遇到的问题搞了很久,百度谷歌就是没有完整的答案,记录下来,方便博友们: 需求:一个简单的W ...

  6. 应对Gradle联网问题、长时间卡在resolve dependencies的思路

    1.出现这种情况,在首先考虑网络问题,依赖下载不下来尝试shadowsocks,未果. 2.检查防火墙问题,更换host,无法解决. 3.新建Gradle工程,依然卡在resolve dependen ...

  7. delphi XE的字符串处理

    最近用delphi xe做了个东西,因为以前一直使用Delphi 7做开发,delphi 7 到delphi XE有了很大的变化,最大的变化就是对Unicode的支持,所以刚开始使用DELPHI XE ...

  8. Entity Framework Plus

    ZZZ Project 这家外国公司,有很多关于.NET和数据访问的项目,有收费的,有开源的,我之前介绍过 Z.ExtensionMethods 一个强大的开源扩展库 就出自该名下,其他有 如下 1. ...

  9. 普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

    普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

  10. js实现数字键盘

    效果图: 1.引入jquery.js文件 2.css样式 <style type="text/css"> #numberkeyboard { border: 1px s ...