Gson学习记录
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学习记录的更多相关文章
- kotlin电商学习记录,好久没来逛逛了
好久没来,一直做毕业设计,用kotlin写一个基于以图搜图的购物app,现在又赶上实习,内容多,时间少,不过前途光明并由贵人指点.加油 kotlin电商学习记录 技术选型 视图层 kotlin-and ...
- Android开发技术周报183学习记录
Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...
- Quartz 学习记录1
原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...
- Java 静态内部类与非静态内部类 学习记录.
目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...
- Apache Shiro 学习记录4
今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...
- UWP学习记录12-应用到应用的通信
UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...
- UWP学习记录11-设计和UI
UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...
- UWP学习记录10-设计和UI之控件和模式7
UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...
- UWP学习记录9-设计和UI之控件和模式6
UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...
随机推荐
- centos运行C程序
gcc -o Hello Hello.c 编译成可执行文件 ./Hello 运行 win上也是一样
- LINUX内核分析第六周学习总结——进程的描述和进程的创建
LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- 每日Scrum(8)
今天:在程序中嵌入剖面图,进行美化 明天:测试分析,找学弟学妹来体验我们的软件 任务看板: 燃尽图:
- wc命令行程序
软件的需求分析 程序处理用户需求的模式为: wc.exe [parameter][filename] 在[parameter]中,用户通过输入参数与程序交互,需实现的功能如下: 1.基本功能 支持 - ...
- ElasticSearch 2 (37) - 信息聚合系列之内存与延时
ElasticSearch 2 (37) - 信息聚合系列之内存与延时 摘要 控制内存使用与延时 版本 elasticsearch版本: elasticsearch-2.x 内容 Fielddata ...
- JSONP使用及注意事项小结
什么是JSONP 三句话总结: 概念:JSONP(JSON with Padding)是JSON的一种"使用模式". 目的:用于解决主流浏览器的跨域数据访问的问题. 原理:利用 & ...
- BZOJ1815 SHOI2006有色图(Polya定理)
置换数量是阶乘级别的,但容易发现本质不同的点的置换数量仅仅是n的整数拆分个数,OEIS(或者写个dp或者暴力)一下会发现不是很大,当n=53时约在3e5左右. 于是暴力枚举点的置换,并且发现根据点的置 ...
- BZOJ2744 HEOI2012朋友圈(二分图匹配)
先考虑B国.容易发现a xor b mod 2=0即二进制末位相同,那么可以据此将所有人分成两部分,每一部分各自是一个完全图.然后再将a or b有奇数个1的边连上,现在需要求的就是这样一个图里的最大 ...
- Merkle Tree 概念
Merkle Tree 概念 来源 https://www.cnblogs.com/fengzhiwu/p/5524324.html /*最近在看Ethereum,其中一个重要的概念是Merkle T ...
- c# DataGridView绑定DataTable对象之后总会多一行
DataGridView 属性 AllowUserToAddRows = false