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. PAT题解-1118. Birds in Forest (25)-(并查集模板题)

    如题... #include <iostream> #include <cstdio> #include <algorithm> #include <stri ...

  2. Linux读书笔记第一周

    1.Unix内核的特点:简洁:抽象:可移植性:进程创建:清晰的层次化结构. Linux内核是一种自由/开源的软件,设计思想与Unix系统相似(一切皆文件,特定的单一用途 & 简单的组合方式) ...

  3. Beta版本冲刺(二)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  4. Winform设置开机启动-操作注册表

    #region 设置开机运行 /// <summary> /// 设置开机运行 /// </summary> /// <param name="R_startP ...

  5. ElasticSearch 2 (33) - 信息聚合系列之聚合过滤

    ElasticSearch 2 (33) - 信息聚合系列之聚合过滤 摘要 聚合范围限定还有一个自然的扩展就是过滤.因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上. 版 ...

  6. python的logging日志模块

    1. 简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info mess ...

  7. 一本通1623Sherlock and His Girlfriend

    1623:Sherlock and His Girlfriend 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:Codeforces Round ...

  8. 题解 P2580 【于是他错误的点名开始了】

    这个题的题解区就没一简单一点的指针题解?(大概是瞎了) So,这篇题解是给那些想用指针而害怕的同学食用的qwq 记得有一篇题解有个dalao作者放了几个静态模拟的trie树结果最后放了个动态的跑路了. ...

  9. 【BZOJ1083】[SCOI2005]繁忙的都市(最小生成树)

    [BZOJ1083][SCOI2005]繁忙的都市(最小生成树) 题面 BZOJ 洛谷 题解 模板题. #include<iostream> #include<cstdio> ...

  10. hive安装笔记

    http://note.youdao.com/noteshare?id=bfbe59aef4ef7c3502975d3d575239e1