归期几何?
前言:
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON 是基于文本的格式,通常用于在客户端和服务器之间传输数据。它的设计目标是简洁和易于理解。
JSON-Java、Gson、Jackson、FastJson等。
这个框架要做的事情有两件:
● 如何把对象序列化为json——原理
● 如何把json序列化为对象——原理
● Gson序列化和反序列化的方式有哪些?都是如何实现的?
● 其实想想应该不难,很容易想到序列化为json,一个String就ok
● 反序列化回来,只需要使用反射就可以了
● 对于这个框架我们需要知道的是,实现方式,设计模式——coding的艺术,建造者,工厂。。。可扩展的编程思想
简单介绍一下json:
what:
1. 对象与 JSON 的相互转换:
序列化: 将 Java 对象转换为 JSON 字符串。
反序列化: 将 JSON 字符串转换为 Java 对象。
2. 支持复杂数据结构: Gson 可以处理复杂的 Java 对象,包括嵌套对象、集合(如 List 和 Map)等。
3. 自定义序列化和反序列化: 你可以通过实现 JsonSerializer 和 JsonDeserializer 接口来自定义对象的序列化和反序列化过程。
4. 灵活的配置: Gson 提供了多种配置选项,例如控制字段的命名策略、忽略某些字段等。
5. 支持泛型: Gson 可以处理带有泛型的类型,能够正确地序列化和反序列化泛型集合。
why:
我们需要在客户端将数据呈现给用户,技术上涉及到将后端发送的Json数据转化为bean类,一般我们需要自己封装实现,现在有了Gson,足矣。
how:
提供两个主要的API:
Gson json = new Gson();
json.toJson();
json.fromJson();
toJson:
我们先从toJson路径理解原理:
首先是toJson的重载方法们:
根据不同参数选择不同的重载方法,最后都会出现到这里:
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
@SuppressWarnings("unchecked")
//1
TypeAdapter<Object> adapter = (TypeAdapter<Object>) getAdapter(TypeToken.get(typeOfSrc));
//2
Strictness oldStrictness = writer.getStrictness();
if (this.strictness != null) {
writer.setStrictness(this.strictness);
} else if (writer.getStrictness() == Strictness.LEGACY_STRICT) {
// For backward compatibility change to LENIENT if writer has default strictness LEGACY_STRICT
writer.setStrictness(Strictness.LENIENT);
}
//3
boolean oldHtmlSafe = writer.isHtmlSafe();
//4
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setHtmlSafe(htmlSafe);
writer.setSerializeNulls(serializeNulls);
try {
//5
adapter.write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (AssertionError e) {
throw new AssertionError(
"AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
} finally {
writer.setStrictness(oldStrictness);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
分析:
● 1处getAdapter是为了拿到对应类的类型适配器。 类型适配器how:根据序列化不同的类选取不同的实现方式。 当然我们也可以根据自己的需要自定义一个类型适配器,细节无需讲解,看例子自然明白:
//官方:
//一个序列化坐标的类型适配器:
//use:Gson 会将Points转换为 JSON 作为像"5,8"这样的字符串
//not use:,而不是像{"x":5,"y":8}
PointAdapter extends TypeAdapter<Point> {
public Point read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
String xy = reader.nextString();
String[] parts = xy.split(",");
int x = Integer.parseInt(parts[0]);
int y = Integer.parseInt(parts[1]);
return new Point(x, y);
}
public void write(JsonWriter writer, Point value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
String xy = value.getX() + "," + value.getY();
writer.value(xy);
}
}
● 2处Strictness这个类用于限定遵守Json格式,先将Gson的严格模式设置为writer的严格模式:有自定义的writer因此。。。
一共有三种模式:
/* Allow large deviations from the JSON specification. /
LENIENT,
/* Allow certain small deviations from the JSON specification for legacy reasons. / LEGACY_STRICT,
/* Strict compliance with the JSON specification. / STRICT 模式默认赋值是null,
// Strictness of null is the legacy mode where some Gson APIs are always lenient
⬆️null就是第二个模式:LEGACY_STRICT
Strictness. STRICT和Strictness. LEGACY_STRICT目前它们的行为是相同的。在这些严格模式下,作者仅按照 RFC 8259 编写 JSON。 rfc8259 通过反射测试,确实为null;
● 3处决定序列化之后作者编写的 JSON 是否可以安全地包含在 HTML 和 XML 文档中,可以则返回 true,也就是转义字符那些:"\u0004"
● 4处 if object members are serialized when their value is null:Returns true , This has no impact on array elements. The default is true.否则忽略
● 5处开始转化为Json,不同的类型适配器有不同的实现。
现在我们看看类型适配器是如何获取的:
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
Objects.requireNonNull(type, "type must not be null");
//1
TypeAdapter<?> cached = typeTokenCache.get(type);
if (cached != null) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) cached;
return adapter;
}
//2
Map<TypeToken<?>, TypeAdapter<?>> threadCalls = threadLocalAdapterResults.get();
boolean isInitialAdapterRequest = false;
if (threadCalls == null) {
threadCalls = new HashMap<>();
threadLocalAdapterResults.set(threadCalls);
isInitialAdapterRequest = true;
} else {
// the key and value type parameters always agree
@SuppressWarnings("unchecked")
TypeAdapter<T> ongoingCall = (TypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
}
TypeAdapter<T> candidate = null;
try {
//3
FutureTypeAdapter<T> call = new FutureTypeAdapter<>();
threadCalls.put(type, call);
//4
for (TypeAdapterFactory factory : factories) {
candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
// Replace future adapter with actual adapter
threadCalls.put(type, candidate);
break;
}
}
} finally {
//5
if (isInitialAdapterRequest) {
threadLocalAdapterResults.remove();
}
}
//6
if (candidate == null) {
throw new IllegalArgumentException(
"GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
}
if (isInitialAdapterRequest) {
/*
* Publish resolved adapters to all threads
* Can only do this for the initial request because cyclic dependency TypeA -> TypeB -> TypeA
* would otherwise publish adapter for TypeB which uses not yet resolved adapter for TypeA
* See https://github.com/google/gson/issues/625
*/
typeTokenCache.putAll(threadCalls);
}
return candidate;
}
分析:
● 1处从缓存ConcurrentHashMap中拿到TypeToken,待会要通过TypeToken拿到对应的TypeAdapter
判断缓存中存在直接返回
● 2处从threadLoacl中拿出threadCalls,映射关系依然是<TypeToken,TypeAdapter>的HashMap,想知道ConcurrentHashMap和HashMap可看:
不存在就初始化,isInitialAdapterRequest = true; 存在就直接返回,return ongoingCall
不存在继续:
● 3处初始化一个FutureTypeAdapter,作为TypeAdapter的代理类,放入缓存集合中
● 4处循环寻找对应type的工厂类,使用工厂创建对应的TypeAdapter
工厂集合初始化在Gson的构造器中,
分为三部分:
1. 系统内置默认的适配器
2. 用户自己添加的适配器
3. 平台基础的适配器
这里选取系统默认的ObjectTypeAdapter的write和read继续接下来的解读
参数是ObjToNum
这里走上面的分支:
进入ObjectTypeAdapter,
public void write(JsonWriter out, Object value) throws IOException {
if (value == null) {
out.nullValue();
} else {
TypeAdapter<Object> typeAdapter = this.gson.getAdapter(value.getClass());
if (typeAdapter instanceof ObjectTypeAdapter) {
out.beginObject();
out.endObject();
} else {
typeAdapter.write(out, value);
}
}
}
开始write,比较容易理解:
fromJson
同样,从重载方法们中找到
public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT)
throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
Strictness oldStrictness = reader.getStrictness();
if (this.strictness != null) {
reader.setStrictness(this.strictness);
} else if (reader.getStrictness() == Strictness.LEGACY_STRICT) {
// For backward compatibility change to LENIENT if reader has default strictness LEGACY_STRICT
reader.setStrictness(Strictness.LENIENT);
}
try {
//1
JsonToken unused = reader.peek();
isEmpty = false;
//2
TypeAdapter<T> typeAdapter = getAdapter(typeOfT);
return typeAdapter.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return null for empty
* documents instead of throwing.
*/
if (isEmpty) {
return null;
}
throw new JsonSyntaxException(e);
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
// TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
throw new JsonSyntaxException(e);
} catch (AssertionError e) {
throw new AssertionError(
"AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e);
} finally {
reader.setStrictness(oldStrictness);
}
}
● 1处使用peek方法拿到上层的类型,但是不消耗。
JsonReader通过stack数组,模拟栈的出栈,就是如此了
● 2处开始读
Gson针对下一个即将读出的行为有一个状态,不同的类型对应的状态不同,用于 JSON 解析器或生成器的内部实现中,帮助跟踪当前的 JSON 结构状态。通过使用这些常量,代码可以更清晰地表达当前的解析状态,从而在处理 JSON 数据时做出相应的决策。
小bug:
对于Object类的处理:
是用这个ObjectTypeAdapter⬇️
适应静态类型仅为“Object”的类型。在序列化时使用 getClass(),在反序列化时使用原语/ Map/ List。
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedTreeMap<String,Object>();
in.beginObject();
while (in.hasNext()) {
map.put(in.nextName(), read(in));
}
in.endObject();
return map;
如果是Obj类型,就会转换为LinkedTreeMap 存储数据,如果想要强转,就会报错
将Object类型向下转型到某一个具体类的时候就会发生映射失败!
通过字符流Read和Writter,(更加迅速,虽然字符也是按照byte存储)
使用一个Map缓存实体类的Type -> TypeAdapter作为映射关系,这样我们就可以在最后转型的时候知道对应的类信息。