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

  我们首先看下例子:

List<MerchantMessage> merchants = new ArrayList<MerchantMessage>();
MerchantMessage m1 = new MerchantMessage();
List<Photo> p1 = new ArrayList<Photo>();
p1.add(new Photo("zcm", "http://zcm.jpg"));
m1.setPhotos(p1);
m1.setAdcode("123");
m1.setAddress(123123);
m1.setBusinessArea("hello");
merchants.add(m1);
ResponseMerchantResult result = new ResponseMerchantResult(0, merchants);
String json = Gson.toJson(result);
     Gson.formJson(json,ResponseMerchantResult.class);

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

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

 @SuppressWarnings("unchecked")
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}

我们来分析下:  

getAdapter(TypeToken.get(typeOfSrc));

这个代码是获取到解析解析的适配器,根据typeOfSrc匹配现有的适配器,以后所有的操作就是这个适配器来完成。
  @SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
if (cached != null) {
return (TypeAdapter<T>) cached;
} Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
boolean requiresThreadLocalCleanup = false;
if (threadCalls == null) {
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
calls.set(threadCalls);
requiresThreadLocalCleanup = true;
} // the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
} try {
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call); for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
throw new IllegalArgumentException("GSON cannot handle " + type);
} finally {
threadCalls.remove(type); if (requiresThreadLocalCleanup) {
calls.remove();
}
}
}

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

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

  

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

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

public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}

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

  public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
} out.beginObject();
try {
for (BoundField boundField : boundFields.values()) {
if (boundField.writeField(value)) {
out.name(boundField.name);
boundField.write(out, value);
}
}
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
out.endObject();
}
}

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

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

public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode; @SuppressWarnings("unchecked")
TypeToken(Type type) {
this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
}

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

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

  首先看几个基本类

public class GenericResult<T>{

    private long l1;

    private double d1;

    private int i1;

    private String s1;

    private List<T> datas;

}
public class Message<T> {

    private List<T> data;

    public List<T> getData() {
return data;
} public void setData(List<T> data) {
this.data = data;
} }
GenericResult<Message> result2 = new GenericResult<Message>();
Message<MerchantMessage> message = new Message();
message.setData(merchants);
result2.setD1(11d);
result2.setI1(1);
result2.setL1(11l);
result2.setS1("11");
result2.setDatas(Arrays.asList(message));
String json2 = JsonTool.toJson(result2);

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

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

我们需要一个辅助类

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

public class TypeMessage implements ParameterizedType{

    private final Class main;

    private final Type[] args;

    public TypeMessage(Class main,Type[] args){
this.main = main;
this.args = args == null ? new Type[0] : args;
} @Override
public Type[] getActualTypeArguments() {
return this.args;
} @Override
public Type getRawType() {
return this.main;
} @Override
public Type getOwnerType() {
return null;
} }
JsonTool.<GenericResult>toJson(json2,
new TypeMessage(
GenericResult.class,
new Type[]{new TypeMessage(Message.class,new Type[]{MerchantMessage.class})}))

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

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


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. 《Linux内核分析》 第三周 构造一个简单的Linux系统MenuOS

    Linux内核分析 第三周 构造一个简单的Linux系统MenuOS 张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/ ...

  2. java实验项目报告

            JAVA课程项目报告   题目:利用java实现一个时钟的小程序 姓名:王国梁 班级:计科13-2 学号:201303011200         利用JAVA实现一个时钟的小程序 1 ...

  3. Central Control Over Distributed Routing阅读笔记

    Central Control Over Distributed Routing 0.ABSTRACT1.Introduction2.Flexible Fibbing3.Augmenting Topo ...

  4. mysql左外连接

    左外连接的概念性不说了,这次就说一说两个表之间的查询步骤是怎么样的? 例如 SELECT ut.id,ut.name,ut.age, ut.sex,ut.status,st.score,st.subj ...

  5. Daily Scrum - 12/0809

    Meeting Minutes (08的Scrum报告放在word里,publish没有成功,所以这是08-09的报告,抱歉…): 卡片翻转的效果确认完成: 按钮蓄力的效果确认完成: 按钮上移的效果确 ...

  6. Alpha冲刺——day3

    Alpha冲刺--day3 作业链接 Alpha冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602634 ...

  7. Alpha冲刺第9天

    Alpha第9天 1.团队成员 郑西坤 031602542 (队长) 陈俊杰 031602504 陈顺兴 031602505 张胜男 031602540 廖钰萍 031602323 雷光游 03160 ...

  8. final 140字评论II

    1.约跑app: 从性能上讲,着重修改了其他组找出的bug,性能上有了很大的提高,增强了实用性. 从功能上讲,该app可以增加用户之间的互动性,有较多的客户群,适合人群不限于青少年和成年人. 从UI上 ...

  9. es6 对象的扩展

    一.现在还有很多浏览器不能直接使用es6语法.特别是手机端的一些低版本的浏览器.都需要用bale转换一下. 但是目前流行的框架中(vue,react,angular).都有自己的脚手架,都能用webp ...

  10. C语言入门:01.C语言概述

    一.计算机和软件常识 1.计算机运行原理 (1)硬件基本组成:硬盘.内存.CPU (2)个部件之间的运作协调(下图)